diff --git a/angular.json b/angular.json
index f0cb141..662396c 100644
--- a/angular.json
+++ b/angular.json
@@ -32,6 +32,7 @@
}
],
"styles": [
+ "@angular/material/prebuilt-themes/magenta-violet.css",
"src/styles.scss"
],
"scripts": []
@@ -55,7 +56,13 @@
"development": {
"optimization": false,
"extractLicenses": false,
- "sourceMap": true
+ "sourceMap": true,
+ "fileReplacements": [
+ {
+ "replace": "src/environments/environment.ts",
+ "with": "src/environments/environment.development.ts"
+ }
+ ]
}
},
"defaultConfiguration": "production"
@@ -91,6 +98,7 @@
}
],
"styles": [
+ "@angular/material/prebuilt-themes/magenta-violet.css",
"src/styles.scss"
],
"scripts": []
diff --git a/package-lock.json b/package-lock.json
index b84e356..044b5e8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,10 +9,12 @@
"version": "0.0.0",
"dependencies": {
"@angular/animations": "^19.1.0",
+ "@angular/cdk": "^19.2.19",
"@angular/common": "^19.1.0",
"@angular/compiler": "^19.1.0",
"@angular/core": "^19.1.0",
"@angular/forms": "^19.1.0",
+ "@angular/material": "^19.2.19",
"@angular/platform-browser": "^19.1.0",
"@angular/platform-browser-dynamic": "^19.1.0",
"@angular/router": "^19.1.0",
@@ -510,6 +512,21 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/@angular/cdk": {
+ "version": "19.2.19",
+ "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-19.2.19.tgz",
+ "integrity": "sha512-PCpJagurPBqciqcq4Z8+3OtKLb7rSl4w/qBJoIMua8CgnrjvA1i+SWawhdtfI1zlY8FSwhzLwXV0CmWWfFzQPg==",
+ "license": "MIT",
+ "dependencies": {
+ "parse5": "^7.1.2",
+ "tslib": "^2.3.0"
+ },
+ "peerDependencies": {
+ "@angular/common": "^19.0.0 || ^20.0.0",
+ "@angular/core": "^19.0.0 || ^20.0.0",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
"node_modules/@angular/cli": {
"version": "19.2.15",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-19.2.15.tgz",
@@ -683,6 +700,23 @@
"rxjs": "^6.5.3 || ^7.4.0"
}
},
+ "node_modules/@angular/material": {
+ "version": "19.2.19",
+ "resolved": "https://registry.npmjs.org/@angular/material/-/material-19.2.19.tgz",
+ "integrity": "sha512-auIE6JUzTIA3LyYklh9J/T7u64crmphxUBgAa0zcOMDog6SYfwbNe9YeLQqua5ek4OUAOdK/BHHfVl5W5iaUoQ==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "peerDependencies": {
+ "@angular/cdk": "19.2.19",
+ "@angular/common": "^19.0.0 || ^20.0.0",
+ "@angular/core": "^19.0.0 || ^20.0.0",
+ "@angular/forms": "^19.0.0 || ^20.0.0",
+ "@angular/platform-browser": "^19.0.0 || ^20.0.0",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
"node_modules/@angular/platform-browser": {
"version": "19.2.14",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-19.2.14.tgz",
@@ -11277,7 +11311,6 @@
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
"integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"entities": "^6.0.0"
@@ -11318,7 +11351,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
- "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
diff --git a/package.json b/package.json
index ab3d1d7..6652cc0 100644
--- a/package.json
+++ b/package.json
@@ -11,10 +11,12 @@
"private": true,
"dependencies": {
"@angular/animations": "^19.1.0",
+ "@angular/cdk": "^19.2.19",
"@angular/common": "^19.1.0",
"@angular/compiler": "^19.1.0",
"@angular/core": "^19.1.0",
"@angular/forms": "^19.1.0",
+ "@angular/material": "^19.2.19",
"@angular/platform-browser": "^19.1.0",
"@angular/platform-browser-dynamic": "^19.1.0",
"@angular/router": "^19.1.0",
@@ -35,4 +37,4 @@
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.7.2"
}
-}
+}
\ No newline at end of file
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 7073f36..e9e03c7 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,347 +1,5 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Hello, {{ title }}
-
Congratulations! Your app is running. 🎉
-
-
-
-
- @for (item of [
- { title: 'Explore the Docs', link: 'https://angular.dev' },
- { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' },
- { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' },
- { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' },
- { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' },
- ]; track item.title) {
-
- {{ item.title }}
-
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-@if(this.error){
-
response:
- {{ this.error }}
-}
-@if (this.response) {
- response:
- {{ this.response }}
-}
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 27363b6..7c7babe 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -9,15 +9,4 @@ import { RouterOutlet } from '@angular/router';
styleUrl: './app.component.scss'
})
export class AppComponent {
- title = 'IISA_web';
- 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)
- })
- }
}
diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts
index dc39edb..3b98114 100644
--- a/src/app/app.routes.ts
+++ b/src/app/app.routes.ts
@@ -1,3 +1,7 @@
import { Routes } from '@angular/router';
+import { RegistrationComponent } from './registration/registration.component';
-export const routes: Routes = [];
+export const routes: Routes = [
+ { path: 'register', component: RegistrationComponent },
+ { path: '', redirectTo: '/register', pathMatch: 'full' },
+];
diff --git a/src/app/candidate-data.service.ts b/src/app/candidate-data.service.ts
new file mode 100644
index 0000000..5b9ec89
--- /dev/null
+++ b/src/app/candidate-data.service.ts
@@ -0,0 +1,19 @@
+import { HttpClient } from "@angular/common/http";
+import { inject, Injectable } from "@angular/core";
+
+@Injectable({
+ providedIn: 'root'
+})
+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)
+ })
+ }
+}
\ No newline at end of file
diff --git a/src/app/image-input/image-input.component.html b/src/app/image-input/image-input.component.html
new file mode 100644
index 0000000..b0fd17e
--- /dev/null
+++ b/src/app/image-input/image-input.component.html
@@ -0,0 +1,28 @@
+
+
Angular material image input
+
+
\ No newline at end of file
diff --git a/src/app/image-input/image-input.component.scss b/src/app/image-input/image-input.component.scss
new file mode 100644
index 0000000..6a3e299
--- /dev/null
+++ b/src/app/image-input/image-input.component.scss
@@ -0,0 +1,49 @@
+.fileUploadContainer {
+ padding: 10px;
+ display: flex;
+ flex-direction: column;
+ margin: 0 auto;
+ width: 150px;
+ height: 150px;
+ border: dashed 1px #979797;
+ text-align: center;
+ justify-content: center;
+
+ img {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+ max-height: 100%;
+ max-width: 100%;
+ }
+
+ .noImageContainet {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ font-size: 11px;
+ button {
+ font-size: 11px;
+ }
+ }
+
+ .deleteButton{
+ position: absolute;
+ z-index: 10;
+ top: -25px;
+ inset-inline-end: -10px;
+ opacity:50%
+ }
+
+ .fileInput {
+ position: absolute;
+ z-index: 9;
+ opacity: 0;
+ height: 100%;
+ width: 100%;
+ left: 0px;
+ top: 0px;
+ cursor: pointer;
+ }
+ }
\ No newline at end of file
diff --git a/src/app/image-input/image-input.component.ts b/src/app/image-input/image-input.component.ts
new file mode 100644
index 0000000..2d75d56
--- /dev/null
+++ b/src/app/image-input/image-input.component.ts
@@ -0,0 +1,47 @@
+import { Component, inject } from '@angular/core';
+import { FormsModule, ReactiveFormsModule, UntypedFormBuilder } from '@angular/forms';
+import { MatButtonModule } from '@angular/material/button';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatInputModule } from '@angular/material/input';
+import { MatIconModule } from '@angular/material/icon';
+import { CommonModule } from '@angular/common';
+
+
+
+
+@Component({
+ selector: 'app-image-input',
+ imports: [
+ CommonModule,
+ MatButtonModule,
+ MatIconModule,
+ MatFormFieldModule,
+ MatInputModule,
+ FormsModule,
+ ReactiveFormsModule
+ ],
+ templateUrl: './image-input.component.html',
+ styleUrl: './image-input.component.scss'
+})
+export class ImageInputComponent {
+
+ fb = inject(UntypedFormBuilder);
+
+ editForm = this.fb.group({
+ photo: []
+ });
+
+
+ setFileData(event: Event): void {
+ const eventTarget: HTMLInputElement | null = event.target as HTMLInputElement | null;
+ if (eventTarget?.files?.[0]) {
+ const file: File = eventTarget.files[0];
+ const reader = new FileReader();
+ reader.addEventListener('load', () => {
+ this.editForm.get('photo')?.setValue(reader.result as string);
+ });
+ reader.readAsDataURL(file);
+ }
+ }
+}
+
diff --git a/src/app/registration/registration.component.html b/src/app/registration/registration.component.html
new file mode 100644
index 0000000..7029104
--- /dev/null
+++ b/src/app/registration/registration.component.html
@@ -0,0 +1,115 @@
+
+
🚀 Spaceflight Candidate Registration
+
+
+
\ No newline at end of file
diff --git a/src/app/registration/registration.component.scss b/src/app/registration/registration.component.scss
new file mode 100644
index 0000000..5b2c22a
--- /dev/null
+++ b/src/app/registration/registration.component.scss
@@ -0,0 +1,164 @@
+/* Cosmic-themed container styling */
+.registration-container {
+ max-width: 900px;
+ margin: 2rem auto;
+ padding: 2.5rem;
+ background: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%);
+ color: #e0e0e0;
+ border-radius: 16px;
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
+ position: relative;
+ overflow: hidden;
+}
+
+/* Cosmic background overlay */
+.registration-container::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0) 70%);
+ opacity: 0.3;
+ pointer-events: none;
+}
+
+/* Form title with space theme */
+.title {
+ text-align: center;
+ font-size: 2.2rem;
+ font-weight: 700;
+ color: #00d4ff;
+ margin-bottom: 2.5rem;
+ text-transform: uppercase;
+ letter-spacing: 3px;
+ text-shadow: 0 0 12px rgba(0, 212, 255, 0.6);
+ animation: glow 2s ease-in-out infinite alternate;
+}
+
+/* Glowing title animation */
+@keyframes glow {
+ from {
+ text-shadow: 0 0 8px rgba(0, 212, 255, 0.4);
+ }
+ to {
+ text-shadow: 0 0 16px rgba(0, 212, 255, 0.8);
+ }
+}
+
+/* Form styling */
+.form {
+ display: flex;
+ flex-direction: column;
+ gap: 2rem;
+}
+
+/* Full-width form fields */
+.full-width {
+ width: 100%;
+}
+
+// /* Material form field customization */
+// ::ng-deep .mat-form-field {
+// .mat-form-field-label {
+// color: #b0bec5;
+// font-weight: 500;
+// }
+
+// .mat-form-field-underline {
+// background-color: #00d4ff !important;
+// }
+
+// .mat-form-field-ripple {
+// background-color: #00d4ff !important;
+// }
+
+// input, textarea {
+// color: #e0e0e0;
+// background-color: rgba(255, 255, 255, 0.08);
+// border-radius: 6px;
+// padding: 0.75rem;
+// transition: background-color 0.3s ease;
+// }
+
+// input:focus, textarea:focus {
+// background-color: rgba(255, 255, 255, 0.12);
+// }
+
+// textarea {
+// resize: vertical;
+// min-height: 80px;
+// }
+// }
+
+/* Error message styling */
+// ::ng-deep .mat-error {
+// color: #ff6b6b;
+// font-size: 0.9rem;
+// }
+
+/* Upload section */
+.upload-section {
+ align-items: center;
+ margin: 2rem 0;
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+
+ .preview {
+ margin-top: 1.5rem;
+ }
+
+ img {
+ width: 250px;
+ height: 250px;
+ border: 4px solid #00d4ff;
+ box-shadow: 0 0 20px rgba(0, 212, 255, 0.4);
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
+ }
+
+ img:hover {
+ transform: scale(1.1);
+ box-shadow: 0 0 25px rgba(0, 212, 255, 0.6);
+ }
+}
+
+/* Responsive design */
+@media (max-width: 768px) {
+ .registration-container {
+ margin: 1.5rem;
+ padding: 2rem;
+ }
+
+ .title {
+ font-size: 1.8rem;
+ margin-bottom: 2rem;
+ }
+
+ .upload-section img {
+ max-width: 140px;
+ }
+
+ // ::ng-deep .mat-raised-button {
+ // padding: 0.75rem 1.5rem;
+ // font-size: 0.95rem;
+ // }
+}
+
+@media (max-width: 480px) {
+ .registration-container {
+ margin: 1rem;
+ padding: 1.5rem;
+ }
+
+ .title {
+ font-size: 1.4rem;
+ margin-bottom: 1.5rem;
+ }
+
+ .upload-section img {
+ max-width: 120px;
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/registration/registration.component.ts b/src/app/registration/registration.component.ts
new file mode 100644
index 0000000..14aefa5
--- /dev/null
+++ b/src/app/registration/registration.component.ts
@@ -0,0 +1,86 @@
+import { Component, inject, OnInit } from '@angular/core';
+import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
+import { CommonModule } from '@angular/common';
+
+import { MatInputModule } from '@angular/material/input';
+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";
+
+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({
+ selector: 'app-registration',
+ imports: [
+ CommonModule,
+ ReactiveFormsModule,
+ MatInputModule,
+ MatButtonModule,
+ MatCardModule,
+ MatFormFieldModule,
+ ImageInputComponent
+],
+ templateUrl: './registration.component.html',
+ styleUrls: ['./registration.component.scss'],
+})
+export class RegistrationComponent implements OnInit {
+
+ 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)]],
+ age: [0, [Validators.required, Validators.min(18), Validators.max(70)]],
+ city: ['', Validators.required],
+ hobbies: ['', [Validators.maxLength(300)]],
+ reason: ['', [Validators.required, Validators.maxLength(300)]],
+ profileImage: this.fb.control(null, Validators.required),
+ });
+
+ ngOnInit(): void {
+ const savedData = localStorage.getItem('registration');
+ if (!savedData) return;
+
+ const parsed = JSON.parse(savedData);
+ const dayMilliseconds = 1000 * 60 * 60 * 24;
+ if (Date.now() - parsed.timestamp < dayMilliseconds * 3) {
+ this.form.patchValue(parsed.data);
+ this.previewUrl = parsed.data.profileImage;
+ } else {
+ localStorage.removeItem('registration');
+ }
+ }
+
+ 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.');
+ }
+ }
+
+ onCheckErrors() {
+ console.log(this.form.controls.age.errors);
+ }
+}
diff --git a/src/environments/environment.development.ts b/src/environments/environment.development.ts
new file mode 100644
index 0000000..f274e5e
--- /dev/null
+++ b/src/environments/environment.development.ts
@@ -0,0 +1 @@
+export const environment = {};
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
new file mode 100644
index 0000000..f274e5e
--- /dev/null
+++ b/src/environments/environment.ts
@@ -0,0 +1 @@
+export const environment = {};
diff --git a/src/index.html b/src/index.html
index bf097f0..6cd995a 100644
--- a/src/index.html
+++ b/src/index.html
@@ -6,6 +6,8 @@
+
+
diff --git a/src/styles.scss b/src/styles.scss
index 90d4ee0..7e7239a 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -1 +1,4 @@
/* You can add global styles to this file, and also import other style files */
+
+html, body { height: 100%; }
+body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }