This commit is contained in:
2025-08-19 21:07:41 +03:00
parent 5326e545a8
commit 61a1ba59cd
20 changed files with 185 additions and 50 deletions

View File

@@ -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
];

View 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>
}

View File

@@ -0,0 +1,9 @@
.listItem {
margin: 2rem;
background-color: aquamarine;
padding: 1rem;
&:hover{
cursor: pointer;
background-color: aqua;
}
}

View 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;
});
}
}

View File

@@ -0,0 +1 @@
{{this.application}}

View 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);
})
}
}

View File

@@ -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<any>(`${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);
}
}

View 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>

View File

View 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 {
}

View File

@@ -13,7 +13,7 @@
<img [src]="previewUrl" alt="Preview" />
</div>
</div> -->
<app-image-input></app-image-input>
<!-- <app-image-input></app-image-input> -->
<mat-form-field appearance="outline" class="full-width">
<mat-label>Full Name</mat-label>
@@ -37,13 +37,13 @@
<mat-form-field appearance="outline" class="full-width">
<mat-label>Phone Number</mat-label>
<input matInput formControlName="phone" placeholder="+972-XXXXXXX"/>
@if (form.get('phone')?.hasError('required')) {
<input matInput formControlName="phoneNumber" placeholder="+972-XXXXXXX"/>
@if (form.get('phoneNumber')?.hasError('required')) {
<mat-error>
Phone number is required
</mat-error>
}
@if (form.get('phone')?.hasError('pattern')){
@if (form.get('phoneNumber')?.hasError('pattern')){
<mat-error>
Invalid phone number
</mat-error>
@@ -62,8 +62,8 @@
<mat-form-field appearance="outline" class="full-width">
<mat-label>City / Region</mat-label>
<input matInput formControlName="city" />
@if (form.get('city')?.hasError('required')) {
<input matInput formControlName="cityOrRegion" />
@if (form.get('cityOrRegion')?.hasError('required')) {
<mat-error>
Field is required
</mat-error>
@@ -82,13 +82,13 @@
<mat-form-field appearance="outline" class="full-width">
<mat-label>Why I am the perfect candidate</mat-label>
<textarea matInput rows="4" formControlName="reason"></textarea>
@if (form.get('reason')?.hasError('required')) {
<textarea matInput rows="4" formControlName="justification"></textarea>
@if (form.get('justification')?.hasError('required')) {
<mat-error>
Field is required
</mat-error>
}
@if (form.get('reason')?.hasError('maxLength')) {
@if (form.get('justification')?.hasError('maxLength')) {
<mat-error>
Maximum length is 300 characters
</mat-error>

View File

@@ -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<string | null>(null, Validators.required),
justification: ['', [Validators.required, Validators.maxLength(300)]],
// profileImage: this.fb.control<string | null>(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() {

View File

@@ -1 +1,3 @@
export const environment = {};
export const environment = {
hostUrl: 'http://localhost:3000'
};

View File

@@ -0,0 +1,3 @@
export const environment = {
hostUrl: 'https://iisa.novikov.click'
};

View File

@@ -1 +1,3 @@
export const environment = {};
export const environment = {
hostUrl: ''
};

View File

@@ -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));