Backend & Frontend mit Mail Versand
This commit is contained in:
@@ -3,24 +3,50 @@ import { CommonModule } from '@angular/common';
|
||||
import { Component } from '@angular/core';
|
||||
import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';
|
||||
import * as AOS from 'aos';
|
||||
import { OverlayService } from './services/overlay.service';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
imports: [CommonModule, RouterOutlet, RouterLink, RouterLinkActive],
|
||||
template: `
|
||||
<router-outlet></router-outlet>
|
||||
`
|
||||
|
||||
<!-- Please Wait Overlay -->
|
||||
<div
|
||||
*ngIf="isLoading$ | async"
|
||||
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div class="text-white text-lg">Please Wait...</div>
|
||||
</div>
|
||||
|
||||
<!-- Success Message Overlay -->
|
||||
<div
|
||||
*ngIf="isSuccess$ | async"
|
||||
class="fixed top-4 right-4 bg-green-500 text-white px-4 py-2 rounded shadow z-50">
|
||||
Message successfully sent
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class AppComponent {
|
||||
isLoading$: Observable<boolean>;
|
||||
isSuccess$: Observable<boolean>;
|
||||
|
||||
constructor(private overlayService: OverlayService) {
|
||||
this.isLoading$ = this.overlayService.loading$;
|
||||
this.isSuccess$ = this.overlayService.success$;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
AOS.init({
|
||||
duration: 1000,
|
||||
once: true
|
||||
once: true,
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
setTimeout(() => {
|
||||
AOS.refresh();
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { provideRouter, Routes } from '@angular/router';
|
||||
import { AppComponent } from './app.component';
|
||||
import { BlogPostComponent } from './components/blog-post.component';
|
||||
import { LandingPageComponent } from './components/landing-page.component';
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
|
||||
|
||||
export const routes: Routes = [
|
||||
{ path: '', component: LandingPageComponent },
|
||||
@@ -11,5 +12,9 @@ export const routes: Routes = [
|
||||
];
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideRouter(routes),provideZoneChangeDetection({ eventCoalescing: true })]
|
||||
providers: [
|
||||
provideHttpClient(withInterceptorsFromDi()),
|
||||
provideRouter(routes),
|
||||
provideZoneChangeDetection({ eventCoalescing: true })
|
||||
]
|
||||
};
|
||||
|
||||
@@ -2,6 +2,19 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
||||
import { OverlayService } from '../services/overlay.service';
|
||||
|
||||
|
||||
interface ErrorResponse {
|
||||
message: Array<{
|
||||
field: string;
|
||||
message: string;
|
||||
}>;
|
||||
error: string;
|
||||
statusCode: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-contact',
|
||||
@@ -14,8 +27,15 @@ import { FormsModule } from '@angular/forms';
|
||||
<p class="mt-4 text-center text-gray-600">We're here to help you with all your IT needs.</p>
|
||||
<div class="mt-12 max-w-lg mx-auto">
|
||||
<form (ngSubmit)="onSubmit()" class="bg-white p-8 rounded-lg shadow" data-aos="fade-up">
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700">Name</label>
|
||||
<div class="mb-4 relative">
|
||||
<label class="block text-gray-700 flex items-center">
|
||||
Name
|
||||
<span *ngIf="errors.name" class="ml-2 text-red-600 cursor-pointer"
|
||||
[title]="errors.name"
|
||||
data-tooltip-target="tooltip-name">
|
||||
ℹ
|
||||
</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
@@ -24,8 +44,15 @@ import { FormsModule } from '@angular/forms';
|
||||
placeholder="Your Name"
|
||||
required>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700">Email</label>
|
||||
<div class="mb-4 relative">
|
||||
<label class="block text-gray-700 flex items-center">
|
||||
Email
|
||||
<span *ngIf="errors.email" class="ml-2 text-red-600 cursor-pointer"
|
||||
[title]="errors.email"
|
||||
data-tooltip-target="tooltip-email">
|
||||
ℹ
|
||||
</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
@@ -34,8 +61,15 @@ import { FormsModule } from '@angular/forms';
|
||||
placeholder="Your Email"
|
||||
required>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700">Message</label>
|
||||
<div class="mb-4 relative">
|
||||
<label class="block text-gray-700 flex items-center">
|
||||
Message
|
||||
<span *ngIf="errors.message" class="ml-2 text-red-600 cursor-pointer"
|
||||
[title]="errors.message"
|
||||
data-tooltip-target="tooltip-message">
|
||||
ℹ
|
||||
</span>
|
||||
</label>
|
||||
<textarea
|
||||
name="message"
|
||||
[(ngModel)]="formData.message"
|
||||
@@ -53,17 +87,40 @@ import { FormsModule } from '@angular/forms';
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
`
|
||||
`,
|
||||
})
|
||||
export class ContactComponent {
|
||||
private hostname = window.location.hostname;
|
||||
private apiBaseUrl = `http://${this.hostname}:3000`;
|
||||
formData = {
|
||||
name: '',
|
||||
email: '',
|
||||
message: ''
|
||||
message: '',
|
||||
};
|
||||
//errors: { [key: string]: string } = {};
|
||||
errors: { name?: string, email?:string, message?:string } = {};
|
||||
|
||||
onSubmit() {
|
||||
console.log('Form submitted:', this.formData);
|
||||
// Implement your form submission logic here
|
||||
constructor(private http: HttpClient, private overlayService: OverlayService) {}
|
||||
|
||||
async onSubmit() {
|
||||
this.errors = {}; // Reset errors
|
||||
this.overlayService.showLoading();
|
||||
try {
|
||||
await lastValueFrom(this.http.post(`${this.apiBaseUrl}/api`, this.formData));
|
||||
this.overlayService.showSuccess();
|
||||
this.formData = { name: '', email: '', message: '' }; // Formular zurücksetzen
|
||||
} catch (error) {
|
||||
if (error instanceof HttpErrorResponse && error.status === 400) {
|
||||
const errorResponse = error.error as ErrorResponse;
|
||||
errorResponse.message.forEach((err) => {
|
||||
this.errors[err.field] = err.message;
|
||||
});
|
||||
} else {
|
||||
// Allgemeine Fehlerbehandlung
|
||||
console.error('Ein unerwarteter Fehler ist aufgetreten:', error);
|
||||
}
|
||||
} finally {
|
||||
this.overlayService.hideLoading();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
29
src/app/services/overlay.service.ts
Normal file
29
src/app/services/overlay.service.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
// src/app/services/overlay.service.ts
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class OverlayService {
|
||||
private loadingSubject = new BehaviorSubject<boolean>(false);
|
||||
loading$ = this.loadingSubject.asObservable();
|
||||
|
||||
private successSubject = new BehaviorSubject<boolean>(false);
|
||||
success$ = this.successSubject.asObservable();
|
||||
|
||||
showLoading() {
|
||||
this.loadingSubject.next(true);
|
||||
}
|
||||
|
||||
hideLoading() {
|
||||
this.loadingSubject.next(false);
|
||||
}
|
||||
|
||||
showSuccess() {
|
||||
this.successSubject.next(true);
|
||||
setTimeout(() => {
|
||||
this.successSubject.next(false);
|
||||
}, 3000); // Erfolgsmeldung nach 3 Sekunden ausblenden
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,54 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@tailwind utilities;
|
||||
|
||||
/* src/styles.css */
|
||||
|
||||
/* Tooltip Container */
|
||||
[data-tooltip-target] {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Tooltip Text */
|
||||
[data-tooltip-target]::after {
|
||||
content: attr(title);
|
||||
position: absolute;
|
||||
bottom: 125%; /* Position über dem Element */
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background-color: rgba(220, 38, 38, 0.9); /* Rot mit Transparenz */
|
||||
color: white;
|
||||
padding: 5px 8px;
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.2s;
|
||||
font-size: 0.875rem;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* Tooltip Pfeil */
|
||||
[data-tooltip-target]::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 115%; /* Position des Pfeils */
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: rgba(220, 38, 38, 0.9) transparent transparent transparent;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.2s;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* Tooltip sichtbar beim Hover */
|
||||
[data-tooltip-target]:hover::after,
|
||||
[data-tooltip-target]:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user