fix
This commit is contained in:
@@ -56,7 +56,6 @@ 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.
|
||||||
@@ -64,9 +63,4 @@ For more information on using the Angular CLI, including detailed command refere
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
-centralize scss check the same styles for differnet buttons
|
|
||||||
|
|
||||||
****animations transitions (prev next cards)
|
|
||||||
|
|
||||||
--DEPLOY AND WRITE README
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,104 +1,140 @@
|
|||||||
<div class="container app-list">
|
<div class="container app-list">
|
||||||
|
<!-- Top Header -->
|
||||||
<div class="header-row">
|
<div class="header-row">
|
||||||
<div class="list-header-top">
|
<div class="list-header-top">
|
||||||
<button mat-button type="button" routerLink="/landing" class="secondary-btn">
|
<button
|
||||||
|
mat-button
|
||||||
|
type="button"
|
||||||
|
routerLink="/landing"
|
||||||
|
class="secondary-btn">
|
||||||
Go Back
|
Go Back
|
||||||
</button>
|
</button>
|
||||||
<h2 class="page-title">Applications</h2>
|
<h2 class="page-title">Applications</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (!isLoading) {
|
<!-- View Toggle -->
|
||||||
<div class="charts-row">
|
<div class="view-toggle">
|
||||||
<div class="chart-container">
|
<button
|
||||||
<h3>Age Distribution</h3>
|
mat-button
|
||||||
<canvas baseChart
|
[class.active]="viewMode() === 'map'"
|
||||||
[data]="ageChartData()"
|
(click)="viewMode.set('map')">
|
||||||
[options]="chartOptions"
|
Map
|
||||||
[type]="'pie'">
|
</button>
|
||||||
</canvas>
|
<button
|
||||||
</div>
|
mat-button
|
||||||
|
[class.active]="viewMode() === 'charts'"
|
||||||
<div class="chart-container">
|
(click)="viewMode.set('charts')">
|
||||||
<h3>City Distribution</h3>
|
Charts
|
||||||
<canvas baseChart
|
</button>
|
||||||
[data]="cityChartData()"
|
</div>
|
||||||
[options]="chartOptions"
|
|
||||||
[type]="'pie'">
|
|
||||||
</canvas>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (!isLoading) {
|
|
||||||
<div class="list-header">
|
|
||||||
<mat-form-field appearance="outline" class="search-field">
|
|
||||||
<mat-label>Search</mat-label>
|
|
||||||
<input
|
|
||||||
matInput
|
|
||||||
[ngModel]="searchTerm()"
|
|
||||||
(ngModelChange)="searchTerm.set($event)"
|
|
||||||
placeholder="Type to search..."
|
|
||||||
/>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="filter-field">
|
|
||||||
<mat-label>City</mat-label>
|
|
||||||
<mat-select
|
|
||||||
[ngModel]="filterCity()"
|
|
||||||
(ngModelChange)="filterCity.set($event)"
|
|
||||||
>
|
|
||||||
<mat-option value="">All</mat-option>
|
|
||||||
@for (city of availableCities(); track city.name) {
|
|
||||||
<mat-option [value]="city.name">{{ city.name }}</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="sort-field">
|
|
||||||
<mat-label>Sort by</mat-label>
|
|
||||||
<mat-select
|
|
||||||
[ngModel]="sortField()"
|
|
||||||
(ngModelChange)="sortField.set($event)"
|
|
||||||
>
|
|
||||||
@for (option of sortFields; track option.value) {
|
|
||||||
<mat-option [value]="option.value">
|
|
||||||
{{ option.viewValue }}
|
|
||||||
</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Map View -->
|
||||||
|
@if (viewMode() === 'map') {
|
||||||
|
<app-candidates-map
|
||||||
|
[candidates]="applicationList()"
|
||||||
|
[cities]="availableCities()">
|
||||||
|
</app-candidates-map>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Charts View -->
|
||||||
|
@if (!isLoading && viewMode() === 'charts') {
|
||||||
|
<div class="charts-row">
|
||||||
|
<div class="chart-container">
|
||||||
|
<h3>Age Distribution</h3>
|
||||||
|
<canvas baseChart
|
||||||
|
[data]="ageChartData()"
|
||||||
|
[options]="chartOptions"
|
||||||
|
[type]="'pie'">
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="chart-container">
|
||||||
|
<h3>City Distribution</h3>
|
||||||
|
<canvas baseChart
|
||||||
|
[data]="cityChartData()"
|
||||||
|
[options]="chartOptions"
|
||||||
|
[type]="'pie'">
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Search / Filters -->
|
||||||
|
@if (!isLoading) {
|
||||||
|
<div class="list-header">
|
||||||
|
<mat-form-field appearance="outline" class="search-field">
|
||||||
|
<mat-label>Search</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
[ngModel]="searchTerm()"
|
||||||
|
(ngModelChange)="searchTerm.set($event)"
|
||||||
|
placeholder="Type to search..." />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="filter-field">
|
||||||
|
<mat-label>City</mat-label>
|
||||||
|
<mat-select
|
||||||
|
[ngModel]="filterCity()"
|
||||||
|
(ngModelChange)="filterCity.set($event)">
|
||||||
|
<mat-option value="">All</mat-option>
|
||||||
|
@for (city of availableCities(); track city.name) {
|
||||||
|
<mat-option [value]="city.name">
|
||||||
|
{{ city.name }}
|
||||||
|
</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="sort-field">
|
||||||
|
<mat-label>Sort by</mat-label>
|
||||||
|
<mat-select
|
||||||
|
[ngModel]="sortField()"
|
||||||
|
(ngModelChange)="sortField.set($event)">
|
||||||
|
@for (option of sortFields; track option.value) {
|
||||||
|
<mat-option [value]="option.value">
|
||||||
|
{{ option.viewValue }}
|
||||||
|
</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Loading State -->
|
||||||
@if (isLoading) {
|
@if (isLoading) {
|
||||||
<div class="loading-container">
|
<div class="loading-container">
|
||||||
<mat-spinner diameter="50"></mat-spinner>
|
<mat-spinner diameter="50"></mat-spinner>
|
||||||
<p class="loading-text">Loading applications...</p>
|
<p class="loading-text">Loading applications...</p>
|
||||||
</div>
|
</div>
|
||||||
} @else {
|
}
|
||||||
|
@else {
|
||||||
|
<!-- Applications List -->
|
||||||
<div class="list">
|
<div class="list">
|
||||||
@for (application of sortedList(); track application.id) {
|
@for (application of sortedList(); track application.id) {
|
||||||
<div class="listItem" [routerLink]="'/application/' + application.id">
|
<div
|
||||||
|
class="listItem"
|
||||||
|
[routerLink]="'/application/' + application.id">
|
||||||
|
|
||||||
@if (application.profileImage) {
|
@if (application.profileImage) {
|
||||||
<img
|
<img
|
||||||
[src]="application.profileImage"
|
[src]="application.profileImage"
|
||||||
alt="{{ application.fullName }}"
|
alt="{{ application.fullName }}"
|
||||||
class="profile-photo"
|
class="profile-photo" />
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<p class="app-name">{{ application.fullName }}</p>
|
<p class="app-name">{{ application.fullName }}</p>
|
||||||
<p>Age: {{ application.age }}</p>
|
<p>Age: {{ application.age }}</p>
|
||||||
<p>City: {{ application.cityOrRegion }}</p>
|
<p>City: {{ application.cityOrRegion }}</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
|
<button
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
color="warn"
|
color="warn"
|
||||||
class="delete-button"
|
class="delete-button"
|
||||||
(click)="onDeleteClick($event ,application.id)"
|
(click)="onDeleteClick($event, application.id)"
|
||||||
[attr.aria-label]="'Delete ' + application.fullName"
|
[attr.aria-label]="'Delete ' + application.fullName">
|
||||||
>
|
|
||||||
<mat-icon>delete</mat-icon>
|
<mat-icon>delete</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -229,3 +229,34 @@ button.secondary-btn:hover {
|
|||||||
0% { background-position: -200% center; }
|
0% { background-position: -200% center; }
|
||||||
100% { background-position: 200% center; }
|
100% { background-position: 200% center; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.view-toggle {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 1rem;
|
||||||
|
margin: 1rem 0 2rem;
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid #00ffff;
|
||||||
|
color: #00ffff;
|
||||||
|
padding: 0.6rem 1.2rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(0, 255, 255, 0.12);
|
||||||
|
box-shadow: 0 0 12px #00ffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: rgba(0, 255, 255, 0.15);
|
||||||
|
box-shadow: 0 0 18px #00ffff;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { ChartData, ChartOptions, Chart, registerables } from 'chart.js';
|
|||||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { CandidatesMapComponent } from '../candidates-map/candidates-map.component';
|
||||||
|
|
||||||
Chart.register(...registerables);
|
Chart.register(...registerables);
|
||||||
|
|
||||||
@@ -29,7 +30,8 @@ Chart.register(...registerables);
|
|||||||
MatInputModule,
|
MatInputModule,
|
||||||
MatProgressSpinnerModule,
|
MatProgressSpinnerModule,
|
||||||
BaseChartDirective,
|
BaseChartDirective,
|
||||||
MatButtonModule
|
MatButtonModule,
|
||||||
|
CandidatesMapComponent,
|
||||||
],
|
],
|
||||||
templateUrl: './application-list.component.html',
|
templateUrl: './application-list.component.html',
|
||||||
styleUrls: ['./application-list.component.scss']
|
styleUrls: ['./application-list.component.scss']
|
||||||
@@ -45,6 +47,7 @@ export class ApplicationListComponent implements OnInit {
|
|||||||
searchTerm = signal('');
|
searchTerm = signal('');
|
||||||
filterCity = signal('');
|
filterCity = signal('');
|
||||||
sortField = signal<string>('fullName');
|
sortField = signal<string>('fullName');
|
||||||
|
viewMode = signal<'map' | 'charts'>('map');
|
||||||
|
|
||||||
availableCities = signal<City[]>(CITY_LIST);
|
availableCities = signal<City[]>(CITY_LIST);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
.map-container {
|
||||||
|
height: 400px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 2rem auto;
|
||||||
|
border: 2px solid #00ffff;
|
||||||
|
border-radius: 14px;
|
||||||
|
box-shadow: 0 0 20px rgba(0, 255, 255, 0.4);
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import { Component, AfterViewInit, Input, ElementRef, ViewChild, OnChanges, SimpleChanges, input } from '@angular/core';
|
||||||
|
import * as L from 'leaflet';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { City } from '../../shared/cities';
|
||||||
|
|
||||||
|
delete (L.Icon.Default.prototype as any)._getIconUrl;
|
||||||
|
|
||||||
|
L.Icon.Default.mergeOptions({
|
||||||
|
iconRetinaUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',
|
||||||
|
iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
|
||||||
|
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
|
||||||
|
});
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-candidates-map',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule],
|
||||||
|
template: `<div class="map-container" #mapEl></div>`,
|
||||||
|
styleUrls: ['./candidates-map.component.scss']
|
||||||
|
})
|
||||||
|
export class CandidatesMapComponent implements AfterViewInit, OnChanges {
|
||||||
|
@ViewChild('mapEl', { static: true }) mapEl!: ElementRef<HTMLDivElement>;
|
||||||
|
candidates = input<any[]>([]);
|
||||||
|
cities = input<City[]>([]);
|
||||||
|
|
||||||
|
map!: L.Map;
|
||||||
|
markersLayer = L.layerGroup();
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
this.map = L.map(this.mapEl.nativeElement).setView([31.7683, 35.2137], 7);
|
||||||
|
|
||||||
|
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
|
maxZoom: 19,
|
||||||
|
attribution: '© OpenStreetMap contributors',
|
||||||
|
}).addTo(this.map);
|
||||||
|
|
||||||
|
this.markersLayer.addTo(this.map);
|
||||||
|
this.renderMarkers();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if ((changes['candidates'] || changes['cities']) && this.map) {
|
||||||
|
this.renderMarkers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMarkers(): void {
|
||||||
|
this.markersLayer.clearLayers();
|
||||||
|
|
||||||
|
if (!this.map || !this.candidates()?.length) return;
|
||||||
|
|
||||||
|
const candidatesPerCity: Record<string, number> = {};
|
||||||
|
this.candidates().forEach(c => {
|
||||||
|
if (c.cityOrRegion) {
|
||||||
|
candidatesPerCity[c.cityOrRegion] = (candidatesPerCity[c.cityOrRegion] || 0) + 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.cities().forEach(city => {
|
||||||
|
const count = candidatesPerCity[city.name];
|
||||||
|
if (!count) return;
|
||||||
|
|
||||||
|
const marker = L.marker([city.lat, city.lng]);
|
||||||
|
marker.bindTooltip(`${city.name}: ${count} candidate(s)`, {
|
||||||
|
permanent: false,
|
||||||
|
direction: 'top'
|
||||||
|
});
|
||||||
|
marker.addTo(this.markersLayer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
<div class="fileUploadWrapper">
|
<div class="fileUploadWrapper">
|
||||||
<mat-label>Profile Photo</mat-label>
|
<mat-label>Profile Photo</mat-label>
|
||||||
|
|
||||||
<div class="fileUploadContainer" [ngStyle]="{'margin-top': value ? '5px' : '20px'}">
|
<div class="fileUploadContainer" [ngStyle]="{ 'margin-top': value ? '5px' : '20px' }">
|
||||||
|
|
||||||
<!-- Image preview -->
|
<!-- Image preview -->
|
||||||
@if (previewUrl) {
|
@if (previewUrl) {
|
||||||
<div class="previewContainer">
|
<div class="previewContainer">
|
||||||
@@ -16,18 +17,22 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- Upload UI when no image -->
|
<!-- Upload UI when no image -->
|
||||||
@if (!previewUrl) {
|
@if (!previewUrl) {
|
||||||
<div class="uploadPrompt">
|
<div class="uploadPrompt">
|
||||||
<mat-icon style="opacity: 60%;">file_upload</mat-icon>
|
<mat-icon style="opacity: 0.6;">file_upload</mat-icon>
|
||||||
<button mat-raised-button color="primary" type="button" (click)="fileInput.click()">
|
<button
|
||||||
|
mat-raised-button
|
||||||
|
color="primary"
|
||||||
|
type="button"
|
||||||
|
(click)="fileInput.click()">
|
||||||
Browse
|
Browse
|
||||||
</button>
|
</button>
|
||||||
<small style="margin: 10px; display:block;">Drag and drop here</small>
|
<small style="margin: 10px 0; display: block;">Drag and drop here</small>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- Hidden file input -->
|
<!-- Hidden file input -->
|
||||||
<input
|
<input
|
||||||
#fileInput
|
#fileInput
|
||||||
class="fileInput"
|
class="fileInput"
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="registration-header">
|
<div class="registration-header">
|
||||||
@if(editMode()){
|
|
||||||
<button mat-button type="button" (click)="cancelEdit()" class="button">
|
<!-- Header buttons -->
|
||||||
Cancel Editing
|
@if(editMode()) {
|
||||||
</button>
|
<button mat-button type="button" (click)="cancelEdit()" class="button">
|
||||||
}@else {
|
Cancel Editing
|
||||||
|
</button>
|
||||||
|
} @else {
|
||||||
<button mat-button type="button" routerLink="/landing" class="button">
|
<button mat-button type="button" routerLink="/landing" class="button">
|
||||||
Go Back
|
Go Back
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
@if(editMode()){
|
|
||||||
<h2>Spaceflight Candidate Registration Update</h2>
|
<!-- Header title -->
|
||||||
}
|
@if(editMode()) {
|
||||||
@else {
|
<h2>Spaceflight Candidate Registration Update</h2>
|
||||||
<h2>Spaceflight Candidate Registration</h2>
|
} @else {
|
||||||
}
|
<h2>Spaceflight Candidate Registration</h2>
|
||||||
</div>
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<form class="registration-form" [formGroup]="form" (ngSubmit)="onSubmit()">
|
<form class="registration-form" [formGroup]="form" (ngSubmit)="onSubmit()">
|
||||||
|
|
||||||
<!-- Profile Image Upload -->
|
<!-- Profile Image Upload -->
|
||||||
|
<div class="image-upload">
|
||||||
<div class="image-upload">
|
<app-image-input [src]="this.previewUrl" formControlName="profileImage"></app-image-input>
|
||||||
<app-image-input [src]="this.previewUrl" formControlName="profileImage"></app-image-input>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Full Name -->
|
<!-- Full Name -->
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>Full Name</mat-label>
|
<mat-label>Full Name</mat-label>
|
||||||
@@ -68,21 +68,21 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<!-- City -->
|
<!-- City -->
|
||||||
|
<app-leaflet-map formControlName="cityOrRegion" class="app-leaflet-map"></app-leaflet-map>
|
||||||
<app-leaflet-map
|
|
||||||
formControlName="cityOrRegion"
|
|
||||||
class="app-leaflet-map">
|
|
||||||
</app-leaflet-map>
|
|
||||||
|
|
||||||
@if (form.get('cityOrRegion')?.invalid && (form.get('cityOrRegion')?.touched || form.get('cityOrRegion')?.dirty)) {
|
@if (form.get('cityOrRegion')?.invalid && (form.get('cityOrRegion')?.touched || form.get('cityOrRegion')?.dirty)) {
|
||||||
@if (form.get('cityOrRegion')?.hasError('required')) {
|
@if (form.get('cityOrRegion')?.hasError('required')) {
|
||||||
<mat-error style="margin-bottom: 8px; padding: 0 16px;">City selection is required</mat-error>
|
<mat-error style="margin-bottom: 8px; padding: 0 16px;">
|
||||||
|
City selection is required
|
||||||
|
</mat-error>
|
||||||
|
}
|
||||||
|
@if (form.get('cityOrRegion')?.hasError('invalidCity')) {
|
||||||
|
<mat-error style="margin-bottom: 8px; padding: 0 16px;">
|
||||||
|
Please select a valid city from the list
|
||||||
|
</mat-error>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@if (form.get('cityOrRegion')?.hasError('invalidCity')) {
|
|
||||||
<mat-error style="margin-bottom: 8px; padding: 0 16px;">Please select a valid city from the list</mat-error>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<!-- Hobbies -->
|
<!-- Hobbies -->
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>Hobbies</mat-label>
|
<mat-label>Hobbies</mat-label>
|
||||||
@@ -104,12 +104,14 @@
|
|||||||
}
|
}
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
<!-- Submit -->
|
<!-- Submit Button -->
|
||||||
<button mat-raised-button color="accent" type="submit">
|
<button mat-raised-button color="accent" type="submit">
|
||||||
{{ editMode() ? 'Update Application' : 'Submit Application' }}
|
{{ editMode() ? 'Update Application' : 'Submit Application' }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
🚀 Powered by <a href="#">Israeli Imaginary Space Agency</a> | © 2025
|
🚀 Powered by <a href="#">Israeli Imaginary Space Agency</a> | © 2025
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user