11<script setup>
2- import { ref } from ' vue' ;
3-
4- const contactInfo = ref ([
5- {
6- type: ' Email' ,
7- 8- icon: ' pi pi-envelope' ,
9- 10- description: ' Best way to reach me for professional inquiries' ,
11- dataTest: ' contact-email'
12- },
13- {
14- type: ' LinkedIn' ,
15- value: ' Jacob Kohl' ,
16- icon: ' pi pi-linkedin' ,
17- link: ' https://linkedin.com/in/jacob-jp-kohl' ,
18- description: ' Connect with me professionally' ,
19- dataTest: ' contact-linkedin'
20- },
21- {
22- type: ' GitHub' ,
23- value: ' jakekohl' ,
24- icon: ' pi pi-github' ,
25- link: ' https://github.com/jakekohl' ,
26- description: ' Check out my code and projects' ,
27- dataTest: ' contact-github'
28- },
29- {
30- type: ' Discord' ,
31- value: ' hawkeye.59' ,
32- icon: ' pi pi-discord' ,
33- link: ' https://github.com/jakekohl#:~:text=https%3A//discordapp.com/users/1097326508814639194' ,
34- description: ' Chat with me on Discord for quick discussions' ,
35- dataTest: ' contact-discord'
36- },
37- {
38- type: ' (Twitter)' ,
39- value: ' @jacobofwonder' ,
40- icon: ' pi pi-twitter' ,
41- link: ' https://x.com/jacobofwonder' ,
42- description: ' I post on X every now and then but it\' s not my main platform' ,
43- dataTest: ' contact-twitter'
44- }
45- ]);
46-
47- const specialties = ref ([
48- {
49- title: ' Test Automation' ,
50- description: ' Cypress, Playwright, and comprehensive testing strategies' ,
51- icon: ' pi pi-cog' ,
52- dataTest: ' contact-test-automation'
53- },
54- {
55- title: ' Consulting & Mentoring' ,
56- description: ' Technical guidance, code reviews, and team mentoring' ,
57- icon: ' pi pi-users' ,
58- dataTest: ' contact-consulting-mentoring'
59- },
60- {
61- title: ' Development Side Projects' ,
62- description: ' Coding, testing, and more! Always learning and growing.' ,
63- icon: ' pi pi-code' ,
64- dataTest: ' contact-development-side-projects'
65- }
66- ]);
2+ import { ref , onMounted } from ' vue' ;
673
68- // const openExternalLink = (url) => {
69- // if (url) {
70- // window.open(url, '_blank', 'noopener,noreferrer');
71- // }
72- // };
4+ const contactInfo = ref ([]);
5+ const specialties = ref ([]);
6+
7+ const fetchContactInfo = async () => {
8+ try {
9+ const response = await fetch (' https://portfolio.jakekohl.dev/contact' );
10+ if (! response .ok ) {
11+ throw new Error (` HTTP error! Status: ${ response .status } ` );
12+ }
13+ const contactResponse = await response .json ();
14+ contactInfo .value = contactResponse .contact ;
15+ specialties .value = contactResponse .specialties ;
16+ } catch (error) {
17+ console .error (' Failed to fetch contact info:' , error);
18+ contactInfo .value = [];
19+ specialties .value = [];
20+ }
21+ };
7322
7423const copyToClipboard = async (text ) => {
7524 try {
7625 await navigator .clipboard .writeText (text);
77- // In a real app, you'd show a toast notification here
78- alert (` Copied ${ text} to clipboard!` );
7926 } catch (err) {
80- console .error (' Failed to copy: ' , err);
27+ console .error (' Failed to copy:' , err);
8128 }
8229};
83- </script >
8430
31+ onMounted (() => {
32+ fetchContactInfo ();
33+ });
34+ </script >
8535<template >
8636 <div class =" contact-container" >
8737 <div class =" content-wrapper" >
@@ -96,8 +46,8 @@ const copyToClipboard = async (text) => {
9646 Ready to collaborate? Let's discuss your next project!
9747 </p >
9848 <p class =" contact-description" >
99- I'm always excited to work on new projects and help bring your ideas to life.
100- Whether you need a full-stack developer, testing specialist, or technical consultant,
49+ I'm always excited to work on new projects and help bring your ideas to life.
50+ Whether you need a full-stack developer, testing specialist, or technical consultant,
10151 I'm here to help.
10252 </p >
10353 </div >
@@ -108,9 +58,9 @@ const copyToClipboard = async (text) => {
10858 <section class =" contact-methods" >
10959 <h2 class =" section-title" >Contact Information</h2 >
11060 <div class =" contact-grid" >
111- <PrimeCard
112- v-for =" contact in contactInfo"
113- :key =" contact.type"
61+ <PrimeCard
62+ v-for =" contact in contactInfo"
63+ :key =" contact.type"
11464 class =" contact-card"
11565 :data-test =" contact.dataTest"
11666 >
@@ -147,9 +97,9 @@ const copyToClipboard = async (text) => {
14797 <section class =" specialties-section" >
14898 <h2 class =" section-title" >What I Can Help You With</h2 >
14999 <div class =" specialties-grid" >
150- <PrimeCard
151- v-for =" specialty in specialties"
152- :key =" specialty.title"
100+ <PrimeCard
101+ v-for =" specialty in specialties"
102+ :key =" specialty.title"
153103 class =" specialty-card"
154104 :data-test =" specialty.dataTest"
155105 >
@@ -371,42 +321,42 @@ const copyToClipboard = async (text) => {
371321 .page-title {
372322 font-size : 2.5rem ;
373323 }
374-
324+
375325 .section-title {
376326 font-size : 2rem ;
377327 }
378-
328+
379329 .contact-grid {
380330 grid-template-columns : 1fr ;
381331 }
382-
332+
383333 .specialties-grid {
384334 grid-template-columns : 1fr ;
385335 }
386-
336+
387337 .contact-card-content {
388338 text-align : center ;
389339 }
390-
340+
391341 .contact-header {
392342 flex-direction : column ;
393343 text-align : center ;
394344 }
395-
345+
396346 .contact-value-wrapper {
397347 justify-content : center ;
398348 }
399-
349+
400350 .contact-actions {
401351 justify-content : center ;
402352 }
403-
353+
404354 .contact-header {
405355 margin-bottom : 3rem ;
406356 }
407-
357+
408358 .contact-methods {
409359 margin-bottom : 3rem ;
410360 }
411361}
412- </style >
362+ </style >
0 commit comments