Merge pull request 'development' (#27) from development into main
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Build and Push Latest Docker Image / build-and-push (push) Successful in 28s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Build and Push Latest Docker Image / build-and-push (push) Successful in 28s
				
			Reviewed-on: #27
This commit was merged in pull request #27.
	This commit is contained in:
		| @@ -39,7 +39,7 @@ | ||||
|     .card .card-content :global(div) { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: space-between; | ||||
|         justify-content: center; | ||||
|         max-width: 100%; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -9,6 +9,47 @@ | ||||
| 	import Loading from "$lib/components/Loading.svelte"; | ||||
| </script> | ||||
|  | ||||
| {#await getJson('/json/me.json')} | ||||
|     <Loading /> | ||||
| {:then info} | ||||
|     <div class="main-card"> | ||||
|         <div class="card-header"> | ||||
|             <h1>{info.name}</h1> | ||||
|             <h3 class="not-required">{info.job_title}</h3> | ||||
|         </div> | ||||
|         <hr /> | ||||
|         <div class="flex-container"> | ||||
|             <img class="profile not-required" src={info.profile_photo} alt="{info.name}'s Profile Photo"> | ||||
|             <p class="about">{@html info.about}</p> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="container"> | ||||
|         <h1>Skills</h1> | ||||
|         <hr /> | ||||
|         <div class="cards"> | ||||
|             <Skills skills="{info.skills}"></Skills> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="container"> | ||||
|         <h1>Experience</h1> | ||||
|         <hr /> | ||||
|         <!-- https://github.com/K-Sato1995/svelte-vertical-timeline --> | ||||
|         <Timeline timelineData="{info.timeline}"></Timeline> | ||||
|     </div> | ||||
|  | ||||
|     <div style="display: none;">{addToast(new Toast("Click on a skill to open a prompt", ToastType.Info, true, 8_000))}</div> | ||||
|     <div style="display: none;">{addToast(new Toast("Welcome!", ToastType.Success, true, 7_000))}</div> | ||||
| {:catch} | ||||
|     <div class="card"> | ||||
|         <div class="card-header"> | ||||
|             <h1>Unable to load portfolio overview data</h1> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div style="display: none;">{addToast(new Toast("Unable to load me.json", ToastType.Error, true, 3000))}</div> | ||||
| {/await} | ||||
|  | ||||
| <style> | ||||
|     .main-card { | ||||
|         background-color: var(--bg-secondary); | ||||
| @@ -63,45 +104,4 @@ | ||||
|         font-size: 1.5em; | ||||
|     } | ||||
|  | ||||
| </style> | ||||
|  | ||||
| {#await getJson('/json/me.json')} | ||||
|     <Loading /> | ||||
| {:then info} | ||||
|     <div class="main-card"> | ||||
|         <div class="card-header"> | ||||
|             <h1>{info.name}</h1> | ||||
|             <h3 class="not-required">{info.job_title}</h3> | ||||
|         </div> | ||||
|         <hr /> | ||||
|         <div class="flex-container"> | ||||
|             <img class="profile not-required" src={info.profile_photo} alt="{info.name}'s Profile Photo"> | ||||
|             <p class="about">{@html info.about}</p> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="container"> | ||||
|         <h1>Skills</h1> | ||||
|         <hr /> | ||||
|         <div class="cards"> | ||||
|             <Skills skills="{info.skills}"></Skills> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="container"> | ||||
|         <h1>Experience</h1> | ||||
|         <hr /> | ||||
|         <!-- https://github.com/K-Sato1995/svelte-vertical-timeline --> | ||||
|         <Timeline timelineData="{info.timeline}"></Timeline> | ||||
|     </div> | ||||
|  | ||||
|     <div style="display: none;">{addToast(new Toast("Click on a skill to open a prompt", ToastType.Info, true, 8_000))}</div> | ||||
|     <div style="display: none;">{addToast(new Toast("Welcome!", ToastType.Success, true, 7_000))}</div> | ||||
| {:catch} | ||||
|     <div class="card"> | ||||
|         <div class="card-header"> | ||||
|             <h1>Unable to load portfolio overview data</h1> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div style="display: none;">{addToast(new Toast("Unable to load me.json", ToastType.Error, true, 3000))}</div> | ||||
| {/await} | ||||
| </style> | ||||
| @@ -3,6 +3,18 @@ | ||||
|     import ThemeSwitcher from "$lib/components/ThemeSwitcher.svelte"; | ||||
| </script> | ||||
|  | ||||
| <nav> | ||||
|     <a href = "/">//Profile</a> | ||||
|     <a href = "/repos">//Repos</a> | ||||
|     <a href = "/contact">//Contact</a> | ||||
|     <ThemeSwitcher /> | ||||
| </nav> | ||||
|  | ||||
| <div class="main-container fade"> | ||||
|     <Toasts /> | ||||
|     <slot /> | ||||
| </div> | ||||
|  | ||||
| <style> | ||||
|     .main-container { | ||||
|         margin-left: 10%; | ||||
| @@ -45,16 +57,4 @@ | ||||
|         0% { opacity: 0; } | ||||
|         100% { opacity: 1; } | ||||
|     } | ||||
| </style> | ||||
|  | ||||
| <nav> | ||||
|     <a href = "/">//Profile</a> | ||||
|     <a href = "/repos">//Repos</a> | ||||
|     <a href = "/contact">//Contact</a> | ||||
|     <ThemeSwitcher /> | ||||
| </nav> | ||||
|  | ||||
| <div class="main-container fade"> | ||||
|     <Toasts /> | ||||
|     <slot /> | ||||
| </div> | ||||
| </style> | ||||
| @@ -1,90 +1,149 @@ | ||||
| <script lang="ts"> | ||||
|     import Card from '$lib/components/Cards/Card.svelte'; | ||||
|     import { Toast, ToastType } from "$lib/toast"; | ||||
|     import { addToast } from "$lib/stores"; | ||||
|     import { Toast, ToastType } from '$lib/toast'; | ||||
|     import { addToast } from '$lib/stores'; | ||||
|  | ||||
|     import { page } from '$app/stores'; | ||||
|     const sent = $page.url.searchParams.get('sent'); | ||||
|  | ||||
|     if (sent == "true") { | ||||
|         addToast(new Toast("Thank you! Your E-Mail has been sent. I will reply as soon as possible!", ToastType.Success, true, 5000)); | ||||
|     if (sent == 'true') { | ||||
|         addToast( | ||||
|             new Toast( | ||||
|                 'Thank you! Your E-Mail has been sent. I will reply as soon as possible!', | ||||
|                 ToastType.Success, | ||||
|                 true, | ||||
|                 5000 | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
|     // Can't use else otherwise the warning will display on load | ||||
|     if (sent == "false") { | ||||
|         addToast(new Toast("Sorry, your E-Mail could not be sent... Please try again later!", ToastType.Error, true, 5000)); | ||||
|     if (sent == 'false') { | ||||
|         addToast( | ||||
|     new Toast( | ||||
|         'Sorry, your E-Mail could not be sent... Please try again later!', | ||||
|         ToastType.Error, | ||||
|         true, | ||||
|         5000 | ||||
|     ) | ||||
|         ); | ||||
|     } | ||||
| </script> | ||||
|  | ||||
| <style> | ||||
|     form { | ||||
|         display: flex; | ||||
|         flex-wrap: wrap; | ||||
|         flex: 2 1; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|         margin: 1em; | ||||
|         gap: 1em 3em; | ||||
|     } | ||||
|      | ||||
|     input, textarea { | ||||
|         padding: 1em 0em 1em 1em; | ||||
|         background-color: var(--input); | ||||
|         color: var(--fg); | ||||
|         border: 1px solid var(--fg);  | ||||
|         outline: 0; | ||||
|         border-radius: 8px; | ||||
|         resize: none; | ||||
|         min-width: 100%; | ||||
|         transition: all 0.15s; | ||||
|         font-size: .8em; | ||||
|     } | ||||
|  | ||||
|     .button { | ||||
|         background-color: var(--fg); | ||||
|         color: var(--bg); | ||||
|         transition: all 0.2s ease-in-out; | ||||
|     } | ||||
|  | ||||
|     .button:hover { | ||||
|         transform: scale(1.01); | ||||
|     } | ||||
|  | ||||
|     textarea { | ||||
|         min-height: 12em; | ||||
|     } | ||||
|  | ||||
|     .container { | ||||
|         display: flex; | ||||
|         flex: 2 1 5rem; | ||||
|         flex-direction: column; | ||||
|         gap: 1em 1em; | ||||
|         width: 100%; | ||||
|     } | ||||
| </style> | ||||
|  | ||||
| <Card> | ||||
|     <div slot="header"> | ||||
|         <h2>Contact</h2> | ||||
|     </div> | ||||
|     <div slot="content"> | ||||
|         <form action="https://api.staticforms.xyz/submit" method="post"> | ||||
|             <div class="container"> | ||||
|                 <input type="hidden" name="accessKey" value="fbb5ec04-506b-448a-a445-a2e47579a966"> | ||||
|                 <input type="text" name="name" placeholder="Name" required> | ||||
|                 <input type="text" name="email" placeholder="Email address" required> | ||||
|                 <input type="hidden" name="replyTo" value="@"> | ||||
|         <form action="https://api.staticforms.xyz/submit" method="post" class="contact-form"> | ||||
|             <input type="hidden" name="accessKey" value="fbb5ec04-506b-448a-a445-a2e47579a966"> | ||||
|  | ||||
|             <!-- Form Items--> | ||||
|             <div class="input-group"> | ||||
|                 <input type="text" id="name" name="name" required placeholder="Your Name" /> | ||||
|                 <input type="email" id="email" name="email" required placeholder="Your Email" /> | ||||
|             </div> | ||||
|             <div class="input-group"> | ||||
|                 <input type="text" name="subject" placeholder="Subject" required> | ||||
|                 <input type="text" name="honeypot" style="display: none;">  | ||||
|             </div> | ||||
|             <div class="container"> | ||||
|                 <textarea name="message" id="message" placeholder="Message" required></textarea> | ||||
|             <div class="input-group"> | ||||
|                 <textarea id="message" name="message" rows="4" required placeholder="Your Message" /> | ||||
|             </div> | ||||
|             <input class="button" type="submit" value="Send" /> | ||||
|              | ||||
|             <!-- Hidden Attributes--> | ||||
|             <input type="hidden" name="replyTo" value="@"> | ||||
|             <input type="text" name="honeypot" style="display: none;"> | ||||
|             <input type="hidden" name="redirectTo" value="https://luke-else.co.uk/contact?sent=true"> | ||||
|              | ||||
|             <!-- reCAPTCHA integration --> | ||||
|             <div class="input-group"> | ||||
|                 <div class="g-recaptcha" data-sitekey="6LfjQAwrAAAAAIF57u8Wt4w5L5vBEWi5DfXXBuGy"></div> | ||||
|                 <script src="https://www.google.com/recaptcha/api.js" async defer></script> | ||||
|             </div> | ||||
|  | ||||
|             <div class="input-group"> | ||||
|                 <button type="submit" class="submit-button">Send Message</button> | ||||
|             </div> | ||||
|         </form> | ||||
|     </div> | ||||
|     <div slot="footer"> | ||||
|         <a href="/Luke Else - CV.pdf" target="_blank" rel="noopener noreferrer">Curriculum Vitae</a> | ||||
|         <a href="mailto:contact@luke-else.co.uk">E-Mail</a> | ||||
|     </div> | ||||
| </Card> | ||||
| </Card> | ||||
|  | ||||
| <style> | ||||
|  | ||||
|     /* Contact form styling */ | ||||
|     .contact-form { | ||||
|         background: none; | ||||
|         padding: 1rem; | ||||
|         width: 80%; | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         gap: 1rem; | ||||
|         transition: all 0.3s ease; | ||||
|     } | ||||
|  | ||||
|     /* Input groups */ | ||||
|     .input-group { | ||||
|         display: flex; | ||||
|         flex-direction: row; | ||||
|         width: 100%; | ||||
|         justify-content: center; | ||||
|         gap: 1rem; | ||||
|         flex-wrap: wrap; | ||||
|         align-items: center; | ||||
|     } | ||||
|  | ||||
|     /* Input fields and textarea */ | ||||
|     .contact-form input, | ||||
|     .contact-form textarea { | ||||
|         padding: 0.8rem 1rem; | ||||
|         border: 1px solid var(--fg); | ||||
|         border-radius: 0.5rem; | ||||
|         background: var(--input); | ||||
|         color: var(--fg); | ||||
|         font-size: 1rem; | ||||
|         transition: border-color 0.3s ease, background 0.3s ease; | ||||
|     } | ||||
|  | ||||
|     .contact-form button { | ||||
|         border: 1px solid var(--fg); | ||||
|         width: 60%; | ||||
|     } | ||||
|  | ||||
|     .contact-form textarea { | ||||
|         width: 100%; | ||||
|         min-width: none; | ||||
|         resize: vertical; | ||||
|         min-height: fit-content; | ||||
|     } | ||||
|  | ||||
|     .contact-form input:focus, | ||||
|     .contact-form textarea:focus { | ||||
|         border-color: var(--glow); | ||||
|         outline: none; | ||||
|     } | ||||
|  | ||||
|     /* Submit button */ | ||||
|     .submit-button { | ||||
|         padding: 0.8rem 1rem; | ||||
|         background: var(--accent); | ||||
|         color: var(--fg); | ||||
|         border: none; | ||||
|         border-radius: 0.5rem; | ||||
|         font-size: 1rem; | ||||
|         cursor: pointer; | ||||
|         transition: background 0.3s ease; | ||||
|     } | ||||
|  | ||||
|     .submit-button:hover { | ||||
|         background: var(--link); | ||||
|         color: var(--input); | ||||
|     } | ||||
|  | ||||
|     .g-recaptcha { | ||||
|         width: fit-content; | ||||
|         overflow: hidden; | ||||
|     } | ||||
| </style> | ||||
|   | ||||
| @@ -8,20 +8,6 @@ | ||||
|     let activeModal: any = null; | ||||
| </script> | ||||
|  | ||||
| <style> | ||||
|     .card-footer { | ||||
|         margin-bottom: 1em; | ||||
|         display: flex; | ||||
|         gap: 1.5em; | ||||
|         justify-content: space-between; | ||||
|     } | ||||
|  | ||||
|     .logo { | ||||
|         color: var(--fg); | ||||
|         font-size: 3em; | ||||
|     } | ||||
| </style> | ||||
|  | ||||
| {#each skills as skill} | ||||
|     <Card on:click={() => {showModal = true; activeModal = skill}}> | ||||
|         <div slot="header"> | ||||
| @@ -56,4 +42,18 @@ | ||||
|             <a href="/repos">Repos</a> | ||||
|         </div> | ||||
|     </Modal> | ||||
| {/if} | ||||
| {/if} | ||||
|  | ||||
| <style> | ||||
|     .card-footer { | ||||
|         margin-bottom: 1em; | ||||
|         display: flex; | ||||
|         gap: 1.5em; | ||||
|         justify-content: space-between; | ||||
|     } | ||||
|  | ||||
|     .logo { | ||||
|         color: var(--fg); | ||||
|         font-size: 3em; | ||||
|     } | ||||
| </style> | ||||
		Reference in New Issue
	
	Block a user