diff --git a/README.md b/README.md index 96842c6..464fdfe 100644 --- a/README.md +++ b/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. ## 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. +-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) +------ diff --git a/angular.json b/angular.json index 662396c..7ea7792 100644 --- a/angular.json +++ b/angular.json @@ -63,6 +63,17 @@ "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" @@ -75,6 +86,9 @@ }, "development": { "buildTarget": "IISA_web:build:development" + }, + "remote-api": { + "buildTarget": "IISA_web:build:remote-api" } }, "defaultConfiguration": "development" diff --git a/package.json b/package.json index 6652cc0..dc542af 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "scripts": { "ng": "ng", "start": "ng serve", + "start:remote-api":"ng serve --configuration=remote-api", "build": "ng build", "watch": "ng build --watch --configuration development", "test": "ng test" diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 3b98114..f436384 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,7 +1,15 @@ import { Routes } from '@angular/router'; 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 = [ - { path: 'register', component: RegistrationComponent }, - { path: '', redirectTo: '/register', pathMatch: 'full' }, + { path: 'landing', component: LandingComponent }, + { path: 'registration', component: RegistrationComponent }, + { path: 'application-list', component: ApplicationListComponent }, + { path: 'application/:id', component: ApplicationComponent }, + { path: '', redirectTo: '/landing', pathMatch: 'full' }, + { path: '**', redirectTo: '/landing' }, + // component: PageNotFoundComponent ]; diff --git a/src/app/application-list/application-list.component.html b/src/app/application-list/application-list.component.html new file mode 100644 index 0000000..2ce37fd --- /dev/null +++ b/src/app/application-list/application-list.component.html @@ -0,0 +1,5 @@ +@for(application of this.applicationList; track application.id){ +
+

{{application.fullName}}

+
+} \ No newline at end of file diff --git a/src/app/application-list/application-list.component.scss b/src/app/application-list/application-list.component.scss new file mode 100644 index 0000000..aa84483 --- /dev/null +++ b/src/app/application-list/application-list.component.scss @@ -0,0 +1,9 @@ +.listItem { + margin: 2rem; + background-color: aquamarine; + padding: 1rem; + &:hover{ + cursor: pointer; + background-color: aqua; + } +} \ No newline at end of file diff --git a/src/app/application-list/application-list.component.ts b/src/app/application-list/application-list.component.ts new file mode 100644 index 0000000..db74ea0 --- /dev/null +++ b/src/app/application-list/application-list.component.ts @@ -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; + }); + + } + +} diff --git a/src/app/application/application.component.html b/src/app/application/application.component.html new file mode 100644 index 0000000..a94ee3c --- /dev/null +++ b/src/app/application/application.component.html @@ -0,0 +1 @@ +{{this.application}} \ No newline at end of file diff --git a/src/app/application/application.component.scss b/src/app/application/application.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/application/application.component.ts b/src/app/application/application.component.ts new file mode 100644 index 0000000..44fb9c7 --- /dev/null +++ b/src/app/application/application.component.ts @@ -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); + }) + } + +} diff --git a/src/app/candidate-data.service.ts b/src/app/candidate-data.service.ts index 5b9ec89..17e33ad 100644 --- a/src/app/candidate-data.service.ts +++ b/src/app/candidate-data.service.ts @@ -1,5 +1,6 @@ import { HttpClient } from "@angular/common/http"; import { inject, Injectable } from "@angular/core"; +import { environment } from "../environments/environment.development"; @Injectable({ providedIn: 'root' @@ -7,13 +8,16 @@ import { inject, Injectable } from "@angular/core"; export class CandidateDataService { httpClient = inject(HttpClient) - response: any | null = null; - error: any | null = null; - sendRequest() { - this.httpClient.get('http://localhost:3000/app/candidates').subscribe({ - next: (res) => this.response = JSON.stringify(res), - error: err => this.error = JSON.stringify(err) - }) + loadCandidateList() { + return this.httpClient.get(`${environment.hostUrl}/app/candidates`); } + getApplicationDetails(id: number) { + return this.httpClient.get(`${environment.hostUrl}/app/candidate/${id}`) + } + submitCandidateForm(formData: Object) { + return this.httpClient.post(`${environment.hostUrl}/app/register`, formData); + } + + } \ No newline at end of file diff --git a/src/app/landing/landing.component.html b/src/app/landing/landing.component.html new file mode 100644 index 0000000..0fbbd11 --- /dev/null +++ b/src/app/landing/landing.component.html @@ -0,0 +1,7 @@ +

landing works!

+ + \ No newline at end of file diff --git a/src/app/landing/landing.component.scss b/src/app/landing/landing.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/landing/landing.component.ts b/src/app/landing/landing.component.ts new file mode 100644 index 0000000..a2026cb --- /dev/null +++ b/src/app/landing/landing.component.ts @@ -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 { + + +} diff --git a/src/app/registration/registration.component.html b/src/app/registration/registration.component.html index 7029104..2a71816 100644 --- a/src/app/registration/registration.component.html +++ b/src/app/registration/registration.component.html @@ -13,7 +13,7 @@ Preview --> - + Full Name @@ -37,13 +37,13 @@ Phone Number - - @if (form.get('phone')?.hasError('required')) { + + @if (form.get('phoneNumber')?.hasError('required')) { Phone number is required } - @if (form.get('phone')?.hasError('pattern')){ + @if (form.get('phoneNumber')?.hasError('pattern')){ Invalid phone number @@ -62,8 +62,8 @@ City / Region - - @if (form.get('city')?.hasError('required')) { + + @if (form.get('cityOrRegion')?.hasError('required')) { Field is required @@ -82,13 +82,13 @@ Why I am the perfect candidate - - @if (form.get('reason')?.hasError('required')) { + + @if (form.get('justification')?.hasError('required')) { Field is required } - @if (form.get('reason')?.hasError('maxLength')) { + @if (form.get('justification')?.hasError('maxLength')) { Maximum length is 300 characters diff --git a/src/app/registration/registration.component.ts b/src/app/registration/registration.component.ts index 14aefa5..b47572d 100644 --- a/src/app/registration/registration.component.ts +++ b/src/app/registration/registration.component.ts @@ -7,6 +7,7 @@ import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatFormFieldModule } from '@angular/material/form-field'; 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})$/; @Component({ @@ -19,24 +20,24 @@ const israeliPhoneRegex = /^(?:(?:(\+?972|\(\+?972\)|\+?\(972\))(?:\s|\.|-)?([1- MatCardModule, MatFormFieldModule, ImageInputComponent -], + ], templateUrl: './registration.component.html', styleUrls: ['./registration.component.scss'], }) export class RegistrationComponent implements OnInit { - + dataService = inject(CandidateDataService); fb = inject(FormBuilder); previewUrl: string | ArrayBuffer | null = null; form = this.fb.group({ fullName: ['', [Validators.required, Validators.minLength(3)]], 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)]], - city: ['', Validators.required], + cityOrRegion: ['', Validators.required], hobbies: ['', [Validators.maxLength(300)]], - reason: ['', [Validators.required, Validators.maxLength(300)]], - profileImage: this.fb.control(null, Validators.required), + justification: ['', [Validators.required, Validators.maxLength(300)]], + // profileImage: this.fb.control(null, Validators.required), }); ngOnInit(): void { @@ -53,31 +54,29 @@ export class RegistrationComponent implements OnInit { } } - onFileSelected(event: any) { - const file = event.target.files[0]; - if (file) { - const reader = new FileReader(); - reader.onload = () => { - if (typeof reader.result === 'string') { - this.previewUrl = reader.result; - this.form.patchValue({ profileImage: reader.result }); - } - }; - reader.readAsDataURL(file); - } - } + // onFileSelected(event: any) { + // const file = event.target.files[0]; + // if (file) { + // const reader = new FileReader(); + // reader.onload = () => { + // if (typeof reader.result === 'string') { + // this.previewUrl = reader.result; + // this.form.patchValue({ profileImage: reader.result }); + // } + // }; + // reader.readAsDataURL(file); + // } + // } onSubmit() { - if (this.form.valid) { - localStorage.setItem( - 'registration', - JSON.stringify({ - data: this.form.value, - timestamp: Date.now(), - }) - ); - alert('✅ Application saved! You can re-edit within 3 days.'); + if (!this.form.valid) { + alert("Invalid Form"); + return; } + 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() { diff --git a/src/environments/environment.development.ts b/src/environments/environment.development.ts index f274e5e..3a769d3 100644 --- a/src/environments/environment.development.ts +++ b/src/environments/environment.development.ts @@ -1 +1,3 @@ -export const environment = {}; +export const environment = { + hostUrl: 'http://localhost:3000' +}; diff --git a/src/environments/environment.remote-api.ts b/src/environments/environment.remote-api.ts new file mode 100644 index 0000000..647037f --- /dev/null +++ b/src/environments/environment.remote-api.ts @@ -0,0 +1,3 @@ +export const environment = { + hostUrl: 'https://iisa.novikov.click' +}; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index f274e5e..839370b 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -1 +1,3 @@ -export const environment = {}; +export const environment = { + hostUrl: '' +}; diff --git a/src/main.ts b/src/main.ts index 35b00f3..fb4555f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,7 @@ import { bootstrapApplication } from '@angular/platform-browser'; import { appConfig } from './app/app.config'; 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));