From 36d43740bfda2dab46f5db3bf948b1ca1bbeae89 Mon Sep 17 00:00:00 2001 From: axherrm Date: Thu, 1 Feb 2024 21:18:44 +0100 Subject: [PATCH] finish contact and footer --- package-lock.json | 13 +- package.json | 3 +- src/app/app.component.html | 4 +- src/app/components/badge/badge.component.scss | 13 -- .../chat-row/chat-row.component.html | 8 +- .../chat-row/chat-row.component.scss | 21 +++- .../components/chat-row/chat-row.component.ts | 9 ++ .../social-media-card.component.html | 11 ++ .../social-media-card.component.scss | 39 ++++++ .../social-media-card.component.ts | 18 +++ src/app/data/data.service.ts | 29 ++++- src/app/data/model.ts | 23 ++++ src/app/sections/about/about.component.html | 1 + .../sections/contact/contact.component.html | 19 ++- .../sections/contact/contact.component.scss | 100 ++++++++------- src/app/sections/contact/contact.component.ts | 118 +++++++++++++++++- .../footer-section.component.html | 17 ++- .../footer-section.component.scss | 25 +++- .../footer-section.component.ts | 11 +- src/app/services/mail.service.ts | 31 +++++ src/assets/svg/EmailJS.svg | 1 + src/data/contact.json | 69 ++++++++-- src/styles.scss | 6 + 23 files changed, 491 insertions(+), 98 deletions(-) create mode 100644 src/app/components/social-media-card/social-media-card.component.html create mode 100644 src/app/components/social-media-card/social-media-card.component.scss create mode 100644 src/app/components/social-media-card/social-media-card.component.ts create mode 100644 src/app/services/mail.service.ts create mode 100644 src/assets/svg/EmailJS.svg diff --git a/package-lock.json b/package-lock.json index 1e79b4f..b9208d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cv", - "version": "0.1", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cv", - "version": "0.1", + "version": "0.1.0", "dependencies": { "@angular/common": "^17.0.7", "@angular/compiler": "^17.0.7", @@ -16,6 +16,7 @@ "@angular/platform-browser": "^17.0.7", "@angular/platform-browser-dynamic": "^17.0.7", "@angular/router": "^17.0.7", + "@emailjs/browser": "^3.12.1", "@studio-freight/lenis": "^1.0.33", "gsap": "^3.12.2", "js-circle-progress": "^1.0.0-beta.0", @@ -2291,6 +2292,14 @@ "node": ">=10.0.0" } }, + "node_modules/@emailjs/browser": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/@emailjs/browser/-/browser-3.12.1.tgz", + "integrity": "sha512-C5nK07CgSCFx3onsuRt/ZaaMvIi0T3SHHanM7fKozjSvbZu+OjHHP9W608fYpic0OavF7yIlfy4+lRDO33JdbA==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@esbuild/android-arm": { "version": "0.19.5", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.5.tgz", diff --git a/package.json b/package.json index 05ed2e9..a6f14ad 100644 --- a/package.json +++ b/package.json @@ -14,10 +14,11 @@ "@angular/compiler": "^17.0.7", "@angular/core": "^17.0.7", "@angular/forms": "^17.0.7", + "@angular/material": "^17.0.7", "@angular/platform-browser": "^17.0.7", "@angular/platform-browser-dynamic": "^17.0.7", "@angular/router": "^17.0.7", - "@angular/material": "^17.0.7", + "@emailjs/browser": "^3.12.1", "@studio-freight/lenis": "^1.0.33", "gsap": "^3.12.2", "js-circle-progress": "^1.0.0-beta.0", diff --git a/src/app/app.component.html b/src/app/app.component.html index 93ff890..8052c76 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,4 +1,4 @@ -
+
@@ -72,8 +72,6 @@

- -
diff --git a/src/app/components/badge/badge.component.scss b/src/app/components/badge/badge.component.scss index c662000..e20fee7 100644 --- a/src/app/components/badge/badge.component.scss +++ b/src/app/components/badge/badge.component.scss @@ -52,20 +52,7 @@ max-height: 100%; display: flex; align-items: center; - //margin-right: 0.3rem; - //max-height: 2.25rem; - //height: 100%; - //width: auto; font-size: 2rem; - - object { - //height: 100%; - //width: auto; - } - - //img { - // height: 100%; - //} } .text { diff --git a/src/app/components/chat-row/chat-row.component.html b/src/app/components/chat-row/chat-row.component.html index a5b6b66..2078081 100644 --- a/src/app/components/chat-row/chat-row.component.html +++ b/src/app/components/chat-row/chat-row.component.html @@ -1,11 +1,11 @@ -
+
-
-
- +
+
+
diff --git a/src/app/components/chat-row/chat-row.component.scss b/src/app/components/chat-row/chat-row.component.scss index dfadc09..6757834 100644 --- a/src/app/components/chat-row/chat-row.component.scss +++ b/src/app/components/chat-row/chat-row.component.scss @@ -2,6 +2,7 @@ position: relative; display: flex; width: 70%; + margin-top: 1.2rem; flex-wrap: nowrap; justify-content: flex-start; align-items: flex-start; @@ -49,6 +50,10 @@ transform: translateX(-1.4rem) rotateZ(-45deg); } } + + &.following-message { + margin-top: 0.3rem; + } } .img-container { @@ -70,15 +75,15 @@ background: linear-gradient(90deg, #a445b2, #fa4299); border-radius: 1rem; padding: 1.5rem; - font-family: 'Noto Sans', sans-serif; - font-size: 1.7rem; - line-height: 1.7rem; text-align: left; + + &.not-received { + filter: brightness(0.5); + } } .triangle { margin-top: calc((var(--1-row-height) - var(--triangle-height)) / 2); - //margin-top: -0.5rem; width: 0; height: 0; } @@ -91,6 +96,14 @@ flex-shrink: 0; background: #a445b2; border-radius: 0.4rem; + + &.not-received { + filter: brightness(0.5); + } +} + +.hidden { + visibility: hidden; } // for real gradient on multiple elements, see: https://codepen.io/axherrm/pen/YzgYEeL diff --git a/src/app/components/chat-row/chat-row.component.ts b/src/app/components/chat-row/chat-row.component.ts index e71b25d..fcc4256 100644 --- a/src/app/components/chat-row/chat-row.component.ts +++ b/src/app/components/chat-row/chat-row.component.ts @@ -12,7 +12,16 @@ import {NgIf} from "@angular/common"; }) export class ChatRowComponent { + @Input() text: string; @Input() side: "left" | "right" = "left"; + /** + * Whether a message is not the first message on the same side of the chat. + */ + @Input() @HostBinding("class.following-message") followingMessage: boolean = false; + /** + * Whether the message is sent successfully. Not sent message are styled differently. + */ + @Input() sent: boolean = true; // @HostBinding("style.flex-direction") flexDirection: string = "row"; @HostBinding("class.left") get left() { return this.isLeft() } diff --git a/src/app/components/social-media-card/social-media-card.component.html b/src/app/components/social-media-card/social-media-card.component.html new file mode 100644 index 0000000..4a6712d --- /dev/null +++ b/src/app/components/social-media-card/social-media-card.component.html @@ -0,0 +1,11 @@ + +
+ +
+ +
+
+ +
+
{{ input.category }}
+
diff --git a/src/app/components/social-media-card/social-media-card.component.scss b/src/app/components/social-media-card/social-media-card.component.scss new file mode 100644 index 0000000..084f3e0 --- /dev/null +++ b/src/app/components/social-media-card/social-media-card.component.scss @@ -0,0 +1,39 @@ +.container { + border-radius: 1.5rem; + padding: 3rem; + background: #262626; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: flex-start; + + color: white; + //max-width: 20vw; +} + +.icon-container { + background: black; + height: 4rem; + width: 4rem; + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + .icon { + aspect-ratio: 1; + color: white; + font-size: 1.7rem; + //display: flex; + //align-items: center; + } + margin-bottom: 3rem; +} + +.username { + font-size: 1.6rem; + text-align: right; +} + +.category { + margin-top: 1rem; +} diff --git a/src/app/components/social-media-card/social-media-card.component.ts b/src/app/components/social-media-card/social-media-card.component.ts new file mode 100644 index 0000000..8c9aaba --- /dev/null +++ b/src/app/components/social-media-card/social-media-card.component.ts @@ -0,0 +1,18 @@ +import {Component, Input} from '@angular/core'; +import {SocialMediaItem} from "../../data/model"; +import {NgIf} from "@angular/common"; + +@Component({ + selector: 'social-media-card', + standalone: true, + imports: [ + NgIf + ], + templateUrl: './social-media-card.component.html', + styleUrl: './social-media-card.component.scss' +}) +export class SocialMediaCardComponent { + + @Input({required: true}) input: SocialMediaItem; + +} diff --git a/src/app/data/data.service.ts b/src/app/data/data.service.ts index a76c074..5dcbf2b 100644 --- a/src/app/data/data.service.ts +++ b/src/app/data/data.service.ts @@ -1,5 +1,14 @@ import {EventEmitter, Injectable} from '@angular/core'; -import {AboutCard, EducationItem, ExperienceItem, LanguagePack, Skill, SkillCategory} from "./model"; +import { + AboutCard, + ContactMessages, + EducationItem, + ExperienceItem, + LanguagePack, + MailSettings, + Skill, + SkillCategory, SocialMediaItem +} from "./model"; import {MenuItem} from "primeng/api"; import * as educationJson from '../../data/education.json'; import * as generalJson from '../../data/general.json'; @@ -8,6 +17,10 @@ import * as skillsJson from '../../data/skills.json'; import * as aboutJson from '../../data/about.json'; import * as contactJson from '../../data/contact.json'; +/** + * Service that imports all the customizable JSON data and stores them. + * Access user data through this service. + */ @Injectable({ providedIn: 'root' }) @@ -16,7 +29,12 @@ export class DataService { defaultLang: string = generalJson.defaultLanguage; loadedLanguages: string[] = generalJson.languages; languagesMenuItems: MenuItem[] = []; + mailSettings: MailSettings = contactJson["mail-settings"]; + socialMedia: SocialMediaItem[] = contactJson["social-media"]; + /** + * Language specific data + */ lang: string; languagePack: LanguagePack; education: EducationItem[]; @@ -24,8 +42,11 @@ export class DataService { skillCategories: SkillCategory[]; skills: Skill[]; about: AboutCard[]; - contact: string[]; + contact: ContactMessages; + /** + * Emitted when the user switches language + */ langChange: EventEmitter = new EventEmitter(true); constructor() { @@ -35,10 +56,10 @@ export class DataService { loadData(): void { console.log("Loading data for lang", this.lang); // @ts-ignore - this.education = educationJson[this.lang]; - // @ts-ignore this.languagePack = new LanguagePack(generalJson[this.lang]); // @ts-ignore + this.education = educationJson[this.lang]; + // @ts-ignore this.experience = experienceJson[this.lang]; // @ts-ignore this.skillCategories = skillsJson[this.lang]; diff --git a/src/app/data/model.ts b/src/app/data/model.ts index dbfa6f2..bd3ffa9 100644 --- a/src/app/data/model.ts +++ b/src/app/data/model.ts @@ -99,3 +99,26 @@ export interface AboutCard { heading?: string; text?: string; } + +export interface MailSettings { + enabled: boolean; + publicKey: string; + serviceId: string; + templateId: string; + ownMessageDelay: number; +} + +export interface ContactMessages { + conversationStart: string[]; + successMessages: string[]; + failedMessages: string[]; + tooManyMessages: string[]; +} + +export interface SocialMediaItem { + category: string; + username: string; + primeIcon?: string; + iconRef?: string; + link: string; +} diff --git a/src/app/sections/about/about.component.html b/src/app/sections/about/about.component.html index 8d9aac6..9e2d938 100644 --- a/src/app/sections/about/about.component.html +++ b/src/app/sections/about/about.component.html @@ -10,6 +10,7 @@ PrimeIcons GSAP Lenis + EmailJS
diff --git a/src/app/sections/contact/contact.component.html b/src/app/sections/contact/contact.component.html index 45986ce..ec75edf 100644 --- a/src/app/sections/contact/contact.component.html +++ b/src/app/sections/contact/contact.component.html @@ -1,14 +1,21 @@ -
+
- {{ message }} - Das ist eine Testmessage + +
-
+ +
- +
-
+
+ + +
diff --git a/src/app/sections/contact/contact.component.scss b/src/app/sections/contact/contact.component.scss index 46688df..f22e119 100644 --- a/src/app/sections/contact/contact.component.scss +++ b/src/app/sections/contact/contact.component.scss @@ -1,3 +1,8 @@ +//$font-family: 'Inter', sans-serif; +//$font-weight: 200; +$font-size: 1.6rem; +$line-height: $font-size; + :host { position: relative; width: 80%; @@ -15,10 +20,12 @@ color: white; height: 100%; width: 100%; + font-family: 'Inter', sans-serif; + font-size: $font-size; + line-height: $line-height; font-weight: 200; - font-size: 1.4rem; - line-height: 1.8rem; + overflow: hidden; display: flex; @@ -39,46 +46,64 @@ form { width: 100%; display: flex; justify-content: center; - align-items: center; + align-items: flex-end; flex-wrap: nowrap; gap: 0.4rem; + $textarea-padding: 0.75rem; + $chat-border: 3px; + .gradient-border { - $border-radius: 2.5rem; - $border: 3px; position: relative; + z-index: 2; width: 80%; - //height: 48px; - background: white; - border-radius: $border-radius; - margin: 1px; - padding: $border calc($border-radius / 2); - - &::before { - // code from: https://dev.to/afif/border-with-gradient-and-radius-387f - content: ""; - position: absolute; - inset: 0; + background: linear-gradient(to right, #a445b2, #fa4299); + border-radius: 2.5rem; + line-height: 0; // Safari & Chrome need this + padding: $chat-border; + + textarea#chat-input { + box-sizing: border-box; + width: 100%; + min-height: calc($line-height + $textarea-padding * 2); + border: 0; + margin: 0; + + resize: none; + overflow: hidden; + + font-family: inherit; + font-weight: inherit; + font-size: inherit; + line-height: $line-height; + border-radius: inherit; - padding: $border; - margin: -1px; - background: linear-gradient(to right, #a445b2, #fa4299); - -webkit-mask: - linear-gradient(#fff 0 0) content-box, - linear-gradient(#fff 0 0); - -webkit-mask-composite: xor; - mask-composite: exclude; + padding: $textarea-padding 1.25rem; + &:focus { + outline: none; + box-shadow: none; + } } } .icon-container { + z-index: 1; background: linear-gradient(to right, #a445b2, #fa4299); - height: 3rem; - width: 3rem; + height: calc($line-height + $textarea-padding * 2 + $chat-border * 2); + width: auto; + aspect-ratio: 1; border-radius: 50%; display: flex; justify-content: center; align-items: center; + border: 0; + + transition: all .1s linear; + &.invisible { + transform: translateX(-150%); + opacity: 0; + visibility: hidden; + } .icon { color: white; @@ -88,22 +113,9 @@ form { } } -:host ::ng-deep { - - .p-inputtext#chat-input { - position: relative; - resize: none; - width: 100%; - height: 100%; - border: 0; - padding-left: 0; - padding-right: 0; - - &:focus { - outline: none; - box-shadow: none; - //box-shadow: 1px 1px 5px rgba(1, 1, 0, .7); - } - } - +.social-media-container { + position: relative; + width: 100%; + display: flex; + gap: 2rem; } diff --git a/src/app/sections/contact/contact.component.ts b/src/app/sections/contact/contact.component.ts index 2c7d252..8e9467d 100644 --- a/src/app/sections/contact/contact.component.ts +++ b/src/app/sections/contact/contact.component.ts @@ -1,8 +1,20 @@ -import { Component } from '@angular/core'; +import {ChangeDetectorRef, Component, ElementRef, HostListener, Renderer2, ViewChild} from '@angular/core'; import {ChatRowComponent} from "../../components/chat-row/chat-row.component"; import {NgForOf} from "@angular/common"; import {DataService} from "../../data/data.service"; -import {InputTextareaModule} from "primeng/inputtextarea"; +import {FormControl, FormsModule, ReactiveFormsModule, Validators} from "@angular/forms"; +import {MailService} from "../../services/mail.service"; +import gsap from "gsap"; +import {ScrollTrigger} from "gsap/ScrollTrigger"; +import {SocialMediaCardComponent} from "../../components/social-media-card/social-media-card.component"; + +gsap.registerPlugin(ScrollTrigger); + +interface Message { + text: string; + side: "left" | "right"; + sent: boolean; +} @Component({ selector: 'contact', @@ -10,13 +22,111 @@ import {InputTextareaModule} from "primeng/inputtextarea"; imports: [ ChatRowComponent, NgForOf, - InputTextareaModule + ReactiveFormsModule, + FormsModule, + SocialMediaCardComponent ], templateUrl: './contact.component.html', styleUrl: './contact.component.scss' }) export class ContactComponent { - constructor(readonly dataService: DataService) {} + @ViewChild("chat_input", {read: ElementRef}) chatInputEl: ElementRef; + @ViewChild("social_media_container", {read: ElementRef}) socialMediaContainer: ElementRef; + @ViewChild("spacer", {read: ElementRef}) spacer: ElementRef; + + messages: Message[] = []; + messagesSent: number = 0; + + text: FormControl = new FormControl("", [Validators.required]); + + protected readonly noop = Function; + + constructor(readonly dataService: DataService, + readonly renderer2: Renderer2, + readonly mailService: MailService, + readonly host: ElementRef, + readonly changeDetectorRef: ChangeDetectorRef) { + } + + ngAfterViewInit() { + ScrollTrigger.create({ + trigger: this.host.nativeElement, + start: "top 80%", + onEnter: () => this.showOwnMessages(this.dataService.contact.conversationStart), + once: true + }) + this.text.valueChanges.subscribe(() => this.resizeChatInput()); + this.adjustSpacing(); + } + + @HostListener('window:resize', ['$event']) + onResize() { + this.adjustSpacing(); + } + + adjustSpacing() { + const lastElementHeight = this.socialMediaContainer.nativeElement.clientHeight; + this.renderer2.setStyle(this.spacer.nativeElement, "height", `calc((100vh - ${lastElementHeight}px) / 2 - 2rem)`); + } + + /** + * Resizes chat input field whenever a new character is inserted to the textarea + */ + resizeChatInput() { + this.renderer2.setStyle(this.chatInputEl.nativeElement, "height", "auto"); + if (this.chatInputEl.nativeElement.scrollHeight !== this.chatInputEl.nativeElement.clientHeight) { + this.renderer2.setStyle(this.chatInputEl.nativeElement, + "height", this.chatInputEl.nativeElement.scrollHeight + "px"); + } + ScrollTrigger.refresh(); + } + + /** + * Called whenever the user wants to send the message. + * Content of textarea gets displayed as new message of the user. + * If delivery of the message via mail successes, the message gets restyled. + * Also triggers responses (for first successful message, failed messages and spam protection). + */ + sendMessage() { + if (!this.text.value || this.text.invalid) { return; } + + const text = this.text.value.replaceAll(/\n/g, "
"); + const newMessage: Message = {text: text, side: "right", sent: false}; + this.messages.push(newMessage); + ScrollTrigger.refresh(); + if (this.messagesSent < 5) { + this.mailService.sendMail(text, + () => { + newMessage.sent = true; + if (this.messagesSent === 0) { + this.showOwnMessages(this.dataService.contact.successMessages); + } + this.messagesSent++; + }, () => { + this.showOwnMessages(this.dataService.contact.failedMessages); + } + ) + } else { + this.showOwnMessages(this.dataService.contact.tooManyMessages); + } + this.text.reset(); + } + /** + * Displays messages of the website owner on the left side of the chat. + * Adds delay for multiple messages as configured in mail settings. + * @param messages + */ + showOwnMessages(messages: string[]) { + let totalDelay = 0; + for (let message of messages) { + setTimeout(() => { + this.messages.push({side: "left", sent: true, text: message}); + this.changeDetectorRef.detectChanges(); + ScrollTrigger.refresh(); + }, totalDelay); + totalDelay += this.dataService.mailSettings.ownMessageDelay; + } + } } diff --git a/src/app/sections/footer-section/footer-section.component.html b/src/app/sections/footer-section/footer-section.component.html index ead61fe..4a846e6 100644 --- a/src/app/sections/footer-section/footer-section.component.html +++ b/src/app/sections/footer-section/footer-section.component.html @@ -1,6 +1,17 @@ diff --git a/src/app/sections/footer-section/footer-section.component.scss b/src/app/sections/footer-section/footer-section.component.scss index 66ca2ef..691fff2 100644 --- a/src/app/sections/footer-section/footer-section.component.scss +++ b/src/app/sections/footer-section/footer-section.component.scss @@ -1,5 +1,7 @@ :host { - position: relative; + position: absolute; + bottom: 0; + left: 0; display: block; width: 100%; padding: 2rem; @@ -17,3 +19,24 @@ footer { .spacer { flex-grow: 1; } + +.button { + padding: 1rem; + background: #262626; + //backdrop-filter: blur(5px); + border-radius: 2rem; + display: flex; + flex-wrap: nowrap; + justify-content: center; + align-items: center; + + color: white; + font-size: 1rem; + box-shadow: 0 0 5px #a445b2, 0 0 5px #fa4299; +} + +.text { + //background: -webkit-linear-gradient(left, #a445b2, #fa4299); + //-webkit-background-clip: text; + //-webkit-text-fill-color: transparent; +} diff --git a/src/app/sections/footer-section/footer-section.component.ts b/src/app/sections/footer-section/footer-section.component.ts index ef3e4c6..e712050 100644 --- a/src/app/sections/footer-section/footer-section.component.ts +++ b/src/app/sections/footer-section/footer-section.component.ts @@ -1,14 +1,21 @@ import { Component } from '@angular/core'; -import {appVersion} from "../../js/global.vars"; +import {appVersion, githubURL} from "../../js/global.vars"; +import {CustomButtonComponent} from "../../components/custom-button/custom-button.component"; @Component({ selector: 'footer-section', standalone: true, - imports: [], + imports: [ + CustomButtonComponent + ], templateUrl: './footer-section.component.html', styleUrl: './footer-section.component.scss' }) export class FooterSectionComponent { protected readonly appVersion = appVersion; + protected readonly githubURL = githubURL; + + currentYear: string = '' + new Date().getFullYear(); + } diff --git a/src/app/services/mail.service.ts b/src/app/services/mail.service.ts new file mode 100644 index 0000000..9fe7730 --- /dev/null +++ b/src/app/services/mail.service.ts @@ -0,0 +1,31 @@ +import {Injectable} from '@angular/core'; +import emailjs from '@emailjs/browser'; +import {DataService} from "../data/data.service"; + +@Injectable({ + providedIn: 'root' +}) +export class MailService { + + constructor(readonly dataService: DataService) {} + + /** + * Sends the given message via configured mail settings and invokes appropriate callback function + * @param message + * @param onSuccess + * @param onFail + */ + async sendMail(message: string, onSuccess: () => void, onFail: () => void) { + emailjs.send(this.dataService.mailSettings.serviceId, this.dataService.mailSettings.templateId, { + message: message, + host: location.host + }, this.dataService.mailSettings.publicKey) + .then(response => { + console.log("Sent message successfully", response.status, response.text); + onSuccess(); + }, error => { + console.error("Sending message failed", error); + onFail(); + }); + } +} diff --git a/src/assets/svg/EmailJS.svg b/src/assets/svg/EmailJS.svg new file mode 100644 index 0000000..e8df7bd --- /dev/null +++ b/src/assets/svg/EmailJS.svg @@ -0,0 +1 @@ + diff --git a/src/data/contact.json b/src/data/contact.json index 7df0509..47509cc 100644 --- a/src/data/contact.json +++ b/src/data/contact.json @@ -1,10 +1,65 @@ { - "de": [ - "Ich hoffe, diese Webseite gefällt Ihnen!", - "Ich freue mich über jegliche Rückmeldungen und Business Anfragen." + "mail-settings": { + "enabled": true, + "publicKey": "0_0q3lt_klW_BfqYj", + "serviceId": "CV", + "templateId": "CV-template", + "ownMessageDelay": 1500 + }, + "social-media": [ + { + "category": "E-Mail", + "username": "axherrm.business@gmail.com", + "primeIcon": "pi-envelope", + "link": "mailto:axherrm.business@gmail.com" + }, + { + "category": "LinkedIn", + "username": "@axherrm", + "primeIcon": "pi-linkedin", + "link": "https://www.linkedin.com/in/axherrm/" + }, + { + "category": "GitHub", + "username": "@axherrm", + "primeIcon": "pi-github", + "link": "https://github.com/axherrm" + } ], - "en": [ - "I hope you like this website!", - "I look forward to any feedback and business inquiries." - ] + "de": { + "conversationStart": [ + "Ich hoffe, diese Webseite gefällt Ihnen!", + "Ich freue mich über jegliche Rückmeldungen und Business Anfragen." + ], + "successMessages": [ + "Vielen Dank für Ihre Nachricht.", + "Falls Sie noch etwas hinzufügen möchten oder Sie vergessen haben anzugeben, wie ich Sie erreichen kann, schreiben Sie einfach eine weitere Nachricht." + ], + "failedMessages": [ + "Ihre Nachricht konnte leider aufgrund von technischen Problemen nicht zugestellt werden.", + "Bitte verwenden sie eine andere Kontaktmöglichkeit stattdessen." + ], + "tooManyMessages": [ + "Zum Schutz vor Spam können maximal fünf Nachrichten versendet werden.", + "Falls es sich nicht um Spam handelt, benutzen Sie bitte eine andere Kontaktmöglichkeit." + ] + }, + "en": { + "conversationStart": [ + "I hope you like this website!", + "I look forward to any feedback and business inquiries." + ], + "successMessages": [ + "Thank you for your message.", + "In case there is anything else you would like to add or if you have forgotten to specify how I can reach you, simply write another message." + ], + "failedMessages": [ + "Unfortunately, your message could not be delivered due to technical problems.", + "Please use another contact option instead." + ], + "tooManyMessages": [ + "To protect against spam, a maximum of five messages may be sent.", + "If it is not spam, simply use another contact option." + ] + } } diff --git a/src/styles.scss b/src/styles.scss index 02f423a..147c4a4 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -22,6 +22,12 @@ html { // TODO remove once style theme is properly done: Anleitung dafür: https://angularindepth.com/posts/1320/custom-theme-for-angular-material-components-series-part-1-create-a-theme :root { + // deactivate link style + a { + color: inherit; + text-decoration: none; + } + --color-grey-dark-1: #363C43; --color-grey-dark-2: #747474; --color-grey-medium-1: #CACACA;