WIP
This commit is contained in:
15
README.md
15
README.md
@@ -55,5 +55,18 @@ ng e2e
|
|||||||
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
||||||
|
|
||||||
## Additional Resources
|
## Additional Resources
|
||||||
|
## TODO
|
||||||
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.
|
||||||
|
-fix picture upload
|
||||||
|
-implement picture loader
|
||||||
|
-use leaflet for map implementation
|
||||||
|
-implement websockets
|
||||||
|
-implement graph
|
||||||
|
-implement total visits
|
||||||
|
-search and filters (by name, city, age, etc.)
|
||||||
|
-navigation between candidates (next/previous) (signals)
|
||||||
|
-edit page
|
||||||
|
-implement live update
|
||||||
|
|
||||||
|
****animations transitions (prev next cards)
|
||||||
|
------
|
||||||
|
|||||||
14
angular.json
14
angular.json
@@ -63,6 +63,17 @@
|
|||||||
"with": "src/environments/environment.development.ts"
|
"with": "src/environments/environment.development.ts"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"remote-api": {
|
||||||
|
"optimization": false,
|
||||||
|
"extractLicenses": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"fileReplacements": [
|
||||||
|
{
|
||||||
|
"replace": "src/environments/environment.ts",
|
||||||
|
"with": "src/environments/environment.remote-api.ts"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultConfiguration": "production"
|
"defaultConfiguration": "production"
|
||||||
@@ -75,6 +86,9 @@
|
|||||||
},
|
},
|
||||||
"development": {
|
"development": {
|
||||||
"buildTarget": "IISA_web:build:development"
|
"buildTarget": "IISA_web:build:development"
|
||||||
|
},
|
||||||
|
"remote-api": {
|
||||||
|
"buildTarget": "IISA_web:build:remote-api"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultConfiguration": "development"
|
"defaultConfiguration": "development"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve",
|
"start": "ng serve",
|
||||||
|
"start:remote-api":"ng serve --configuration=remote-api",
|
||||||
"build": "ng build",
|
"build": "ng build",
|
||||||
"watch": "ng build --watch --configuration development",
|
"watch": "ng build --watch --configuration development",
|
||||||
"test": "ng test"
|
"test": "ng test"
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
import { RegistrationComponent } from './registration/registration.component';
|
import { RegistrationComponent } from './registration/registration.component';
|
||||||
|
import { LandingComponent } from './landing/landing.component';
|
||||||
|
import { ApplicationListComponent } from './application-list/application-list.component';
|
||||||
|
import { ApplicationComponent } from './application/application.component';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{ path: 'register', component: RegistrationComponent },
|
{ path: 'landing', component: LandingComponent },
|
||||||
{ path: '', redirectTo: '/register', pathMatch: 'full' },
|
{ path: 'registration', component: RegistrationComponent },
|
||||||
|
{ path: 'application-list', component: ApplicationListComponent },
|
||||||
|
{ path: 'application/:id', component: ApplicationComponent },
|
||||||
|
{ path: '', redirectTo: '/landing', pathMatch: 'full' },
|
||||||
|
{ path: '**', redirectTo: '/landing' },
|
||||||
|
// component: PageNotFoundComponent
|
||||||
];
|
];
|
||||||
|
|||||||
5
src/app/application-list/application-list.component.html
Normal file
5
src/app/application-list/application-list.component.html
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
@for(application of this.applicationList; track application.id){
|
||||||
|
<div class="listItem" [routerLink]="'/application/'+ application.id">
|
||||||
|
<p>{{application.fullName}}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
9
src/app/application-list/application-list.component.scss
Normal file
9
src/app/application-list/application-list.component.scss
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
.listItem {
|
||||||
|
margin: 2rem;
|
||||||
|
background-color: aquamarine;
|
||||||
|
padding: 1rem;
|
||||||
|
&:hover{
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: aqua;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/app/application-list/application-list.component.ts
Normal file
23
src/app/application-list/application-list.component.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { Component, inject, OnInit } from '@angular/core';
|
||||||
|
import { CandidateDataService } from '../candidate-data.service';
|
||||||
|
import { RouterLink } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-application-list',
|
||||||
|
imports: [RouterLink],
|
||||||
|
templateUrl: './application-list.component.html',
|
||||||
|
styleUrl: './application-list.component.scss'
|
||||||
|
})
|
||||||
|
export class ApplicationListComponent implements OnInit {
|
||||||
|
|
||||||
|
dataService = inject(CandidateDataService);
|
||||||
|
applicationList: any[] = [];
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.dataService.loadCandidateList().subscribe((data) => {
|
||||||
|
this.applicationList = data;
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
1
src/app/application/application.component.html
Normal file
1
src/app/application/application.component.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{{this.application}}
|
||||||
0
src/app/application/application.component.scss
Normal file
0
src/app/application/application.component.scss
Normal file
28
src/app/application/application.component.ts
Normal file
28
src/app/application/application.component.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Component, inject, OnInit } from '@angular/core';
|
||||||
|
import { CandidateDataService } from '../candidate-data.service';
|
||||||
|
import { ActivatedRoute, RouterModule } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-application',
|
||||||
|
imports: [RouterModule],
|
||||||
|
templateUrl: './application.component.html',
|
||||||
|
styleUrl: './application.component.scss'
|
||||||
|
})
|
||||||
|
export class ApplicationComponent implements OnInit {
|
||||||
|
dataService = inject(CandidateDataService);
|
||||||
|
activatedRoute = inject(ActivatedRoute);
|
||||||
|
application: any = {};
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
const id = this.activatedRoute.snapshot.paramMap.get('id');
|
||||||
|
if (!id) {
|
||||||
|
alert('invalid route');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const applicationId = Number.parseInt(id, 10);
|
||||||
|
this.dataService.getApplicationDetails(applicationId).subscribe((data) => {
|
||||||
|
this.application = JSON.stringify(data);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { HttpClient } from "@angular/common/http";
|
import { HttpClient } from "@angular/common/http";
|
||||||
import { inject, Injectable } from "@angular/core";
|
import { inject, Injectable } from "@angular/core";
|
||||||
|
import { environment } from "../environments/environment.development";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -7,13 +8,16 @@ import { inject, Injectable } from "@angular/core";
|
|||||||
export class CandidateDataService {
|
export class CandidateDataService {
|
||||||
httpClient = inject(HttpClient)
|
httpClient = inject(HttpClient)
|
||||||
|
|
||||||
response: any | null = null;
|
|
||||||
error: any | null = null;
|
|
||||||
|
|
||||||
sendRequest() {
|
loadCandidateList() {
|
||||||
this.httpClient.get('http://localhost:3000/app/candidates').subscribe({
|
return this.httpClient.get<any>(`${environment.hostUrl}/app/candidates`);
|
||||||
next: (res) => this.response = JSON.stringify(res),
|
|
||||||
error: err => this.error = JSON.stringify(err)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
getApplicationDetails(id: number) {
|
||||||
|
return this.httpClient.get(`${environment.hostUrl}/app/candidate/${id}`)
|
||||||
|
}
|
||||||
|
submitCandidateForm(formData: Object) {
|
||||||
|
return this.httpClient.post(`${environment.hostUrl}/app/register`, formData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
7
src/app/landing/landing.component.html
Normal file
7
src/app/landing/landing.component.html
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<p>landing works!</p>
|
||||||
|
<button mat-button routerLink="/registration">
|
||||||
|
to registration
|
||||||
|
</button>
|
||||||
|
<button mat-button routerLink="/application-list">
|
||||||
|
to application list
|
||||||
|
</button>
|
||||||
0
src/app/landing/landing.component.scss
Normal file
0
src/app/landing/landing.component.scss
Normal file
15
src/app/landing/landing.component.ts
Normal file
15
src/app/landing/landing.component.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { RouterLink } from '@angular/router';
|
||||||
|
import { RouterLinkActive } from "../../../node_modules/@angular/router/router_module.d-Bx9ArA6K";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-landing',
|
||||||
|
imports: [MatButtonModule, RouterLink,],
|
||||||
|
templateUrl: './landing.component.html',
|
||||||
|
styleUrl: './landing.component.scss'
|
||||||
|
})
|
||||||
|
export class LandingComponent {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<img [src]="previewUrl" alt="Preview" />
|
<img [src]="previewUrl" alt="Preview" />
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
<app-image-input></app-image-input>
|
<!-- <app-image-input></app-image-input> -->
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="full-width">
|
<mat-form-field appearance="outline" class="full-width">
|
||||||
<mat-label>Full Name</mat-label>
|
<mat-label>Full Name</mat-label>
|
||||||
@@ -37,13 +37,13 @@
|
|||||||
|
|
||||||
<mat-form-field appearance="outline" class="full-width">
|
<mat-form-field appearance="outline" class="full-width">
|
||||||
<mat-label>Phone Number</mat-label>
|
<mat-label>Phone Number</mat-label>
|
||||||
<input matInput formControlName="phone" placeholder="+972-XXXXXXX"/>
|
<input matInput formControlName="phoneNumber" placeholder="+972-XXXXXXX"/>
|
||||||
@if (form.get('phone')?.hasError('required')) {
|
@if (form.get('phoneNumber')?.hasError('required')) {
|
||||||
<mat-error>
|
<mat-error>
|
||||||
Phone number is required
|
Phone number is required
|
||||||
</mat-error>
|
</mat-error>
|
||||||
}
|
}
|
||||||
@if (form.get('phone')?.hasError('pattern')){
|
@if (form.get('phoneNumber')?.hasError('pattern')){
|
||||||
<mat-error>
|
<mat-error>
|
||||||
Invalid phone number
|
Invalid phone number
|
||||||
</mat-error>
|
</mat-error>
|
||||||
@@ -62,8 +62,8 @@
|
|||||||
|
|
||||||
<mat-form-field appearance="outline" class="full-width">
|
<mat-form-field appearance="outline" class="full-width">
|
||||||
<mat-label>City / Region</mat-label>
|
<mat-label>City / Region</mat-label>
|
||||||
<input matInput formControlName="city" />
|
<input matInput formControlName="cityOrRegion" />
|
||||||
@if (form.get('city')?.hasError('required')) {
|
@if (form.get('cityOrRegion')?.hasError('required')) {
|
||||||
<mat-error>
|
<mat-error>
|
||||||
Field is required
|
Field is required
|
||||||
</mat-error>
|
</mat-error>
|
||||||
@@ -82,13 +82,13 @@
|
|||||||
|
|
||||||
<mat-form-field appearance="outline" class="full-width">
|
<mat-form-field appearance="outline" class="full-width">
|
||||||
<mat-label>Why I am the perfect candidate</mat-label>
|
<mat-label>Why I am the perfect candidate</mat-label>
|
||||||
<textarea matInput rows="4" formControlName="reason"></textarea>
|
<textarea matInput rows="4" formControlName="justification"></textarea>
|
||||||
@if (form.get('reason')?.hasError('required')) {
|
@if (form.get('justification')?.hasError('required')) {
|
||||||
<mat-error>
|
<mat-error>
|
||||||
Field is required
|
Field is required
|
||||||
</mat-error>
|
</mat-error>
|
||||||
}
|
}
|
||||||
@if (form.get('reason')?.hasError('maxLength')) {
|
@if (form.get('justification')?.hasError('maxLength')) {
|
||||||
<mat-error>
|
<mat-error>
|
||||||
Maximum length is 300 characters
|
Maximum length is 300 characters
|
||||||
</mat-error>
|
</mat-error>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { MatButtonModule } from '@angular/material/button';
|
|||||||
import { MatCardModule } from '@angular/material/card';
|
import { MatCardModule } from '@angular/material/card';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { ImageInputComponent } from "../image-input/image-input.component";
|
import { ImageInputComponent } from "../image-input/image-input.component";
|
||||||
|
import { CandidateDataService } from '../candidate-data.service';
|
||||||
|
|
||||||
const israeliPhoneRegex = /^(?:(?:(\+?972|\(\+?972\)|\+?\(972\))(?:\s|\.|-)?([1-9]\d?))|(0[23489]{1})|(0[57]{1}[0-9]))(?:\s|\.|-)?([^0\D]{1}\d{2}(?:\s|\.|-)?\d{4})$/;
|
const israeliPhoneRegex = /^(?:(?:(\+?972|\(\+?972\)|\+?\(972\))(?:\s|\.|-)?([1-9]\d?))|(0[23489]{1})|(0[57]{1}[0-9]))(?:\s|\.|-)?([^0\D]{1}\d{2}(?:\s|\.|-)?\d{4})$/;
|
||||||
@Component({
|
@Component({
|
||||||
@@ -24,19 +25,19 @@ const israeliPhoneRegex = /^(?:(?:(\+?972|\(\+?972\)|\+?\(972\))(?:\s|\.|-)?([1-
|
|||||||
styleUrls: ['./registration.component.scss'],
|
styleUrls: ['./registration.component.scss'],
|
||||||
})
|
})
|
||||||
export class RegistrationComponent implements OnInit {
|
export class RegistrationComponent implements OnInit {
|
||||||
|
dataService = inject(CandidateDataService);
|
||||||
fb = inject(FormBuilder);
|
fb = inject(FormBuilder);
|
||||||
previewUrl: string | ArrayBuffer | null = null;
|
previewUrl: string | ArrayBuffer | null = null;
|
||||||
|
|
||||||
form = this.fb.group({
|
form = this.fb.group({
|
||||||
fullName: ['', [Validators.required, Validators.minLength(3)]],
|
fullName: ['', [Validators.required, Validators.minLength(3)]],
|
||||||
email: ['', [Validators.required, Validators.email]],
|
email: ['', [Validators.required, Validators.email]],
|
||||||
phone: ['', [Validators.required, Validators.pattern(israeliPhoneRegex)]],
|
phoneNumber: ['', [Validators.required, Validators.pattern(israeliPhoneRegex)]],
|
||||||
age: [0, [Validators.required, Validators.min(18), Validators.max(70)]],
|
age: [0, [Validators.required, Validators.min(18), Validators.max(70)]],
|
||||||
city: ['', Validators.required],
|
cityOrRegion: ['', Validators.required],
|
||||||
hobbies: ['', [Validators.maxLength(300)]],
|
hobbies: ['', [Validators.maxLength(300)]],
|
||||||
reason: ['', [Validators.required, Validators.maxLength(300)]],
|
justification: ['', [Validators.required, Validators.maxLength(300)]],
|
||||||
profileImage: this.fb.control<string | null>(null, Validators.required),
|
// profileImage: this.fb.control<string | null>(null, Validators.required),
|
||||||
});
|
});
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@@ -53,31 +54,29 @@ export class RegistrationComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onFileSelected(event: any) {
|
// onFileSelected(event: any) {
|
||||||
const file = event.target.files[0];
|
// const file = event.target.files[0];
|
||||||
if (file) {
|
// if (file) {
|
||||||
const reader = new FileReader();
|
// const reader = new FileReader();
|
||||||
reader.onload = () => {
|
// reader.onload = () => {
|
||||||
if (typeof reader.result === 'string') {
|
// if (typeof reader.result === 'string') {
|
||||||
this.previewUrl = reader.result;
|
// this.previewUrl = reader.result;
|
||||||
this.form.patchValue({ profileImage: reader.result });
|
// this.form.patchValue({ profileImage: reader.result });
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
reader.readAsDataURL(file);
|
// reader.readAsDataURL(file);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
if (this.form.valid) {
|
if (!this.form.valid) {
|
||||||
localStorage.setItem(
|
alert("Invalid Form");
|
||||||
'registration',
|
return;
|
||||||
JSON.stringify({
|
|
||||||
data: this.form.value,
|
|
||||||
timestamp: Date.now(),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
alert('✅ Application saved! You can re-edit within 3 days.');
|
|
||||||
}
|
}
|
||||||
|
console.log(JSON.stringify(this.form.value));
|
||||||
|
this.dataService.submitCandidateForm(this.form.value).subscribe(() => {
|
||||||
|
alert('✅ Application saved! You can re-edit within 3 days.')
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onCheckErrors() {
|
onCheckErrors() {
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
export const environment = {};
|
export const environment = {
|
||||||
|
hostUrl: 'http://localhost:3000'
|
||||||
|
};
|
||||||
|
|||||||
3
src/environments/environment.remote-api.ts
Normal file
3
src/environments/environment.remote-api.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export const environment = {
|
||||||
|
hostUrl: 'https://iisa.novikov.click'
|
||||||
|
};
|
||||||
@@ -1 +1,3 @@
|
|||||||
export const environment = {};
|
export const environment = {
|
||||||
|
hostUrl: ''
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { bootstrapApplication } from '@angular/platform-browser';
|
import { bootstrapApplication } from '@angular/platform-browser';
|
||||||
import { appConfig } from './app/app.config';
|
import { appConfig } from './app/app.config';
|
||||||
import { AppComponent } from './app/app.component';
|
import { AppComponent } from './app/app.component';
|
||||||
|
import { environment } from './environments/environment';
|
||||||
|
|
||||||
bootstrapApplication(AppComponent, appConfig)
|
bootstrapApplication(AppComponent, appConfig).then(()=>{console.log(JSON.stringify(environment))})
|
||||||
.catch((err) => console.error(err));
|
.catch((err) => console.error(err));
|
||||||
|
|||||||
Reference in New Issue
Block a user