diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
index de9dd16..549c086 100644
--- a/src/components/Footer.tsx
+++ b/src/components/Footer.tsx
@@ -5,7 +5,7 @@ import { BsCode } from 'react-icons/bs';
import { FaReact } from 'react-icons/fa';
import { HiOutlineMail } from 'react-icons/hi';
-import { contactInfo } from '~/content';
+import { useContactInfo } from '~/store/contact';
const FooterItem = ({
icon,
@@ -33,30 +33,40 @@ const FooterItem = ({
};
const Footer = () => {
+ const { data: contact_info, isLoading } = useContactInfo();
+
return (
-
-
-
-
-
-
-
-
-
+ {isLoading ? (
+ 'Loading...'
+ ) : !contact_info ? (
+ 'Oops...'
+ ) : (
+ <>
+
+
+
+
+
+
+
+
+
+ >
+ )}
);
};
diff --git a/src/components/Job.tsx b/src/components/Job.tsx
deleted file mode 100644
index 37b0490..0000000
--- a/src/components/Job.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import { BsDot } from 'react-icons/bs';
-
-import { JobType } from '~/content';
-import SubHeading from '~/components/SubHeading';
-
-const Job = ({ job }: { job: JobType }) => {
- return (
-
-
-
- {job.company}
-
- {job.duration}
-
-
{job.description}
- {job.responsibilities.map((resp) => (
-
- {resp}
-
- ))}
-
- );
-};
-
-export default Job;
diff --git a/src/components/Skills.tsx b/src/components/Skills.tsx
index 2b20575..26b349f 100644
--- a/src/components/Skills.tsx
+++ b/src/components/Skills.tsx
@@ -1,22 +1,25 @@
-import { technicalSkills, professionalSkills } from '~/content';
+import { useLeadershipSkills, useTechnicalSkills } from '~/store/skills';
import Section from './Section';
import SubHeading from './SubHeading';
const Skills = () => {
+ const professionalSkills = useLeadershipSkills();
+ const technicalSkills = useTechnicalSkills();
+
return (
-
- {technicalSkills.map((skill) => (
-
- {skill}
+
+ {technicalSkills.data?.map((skill) => (
+
+ {skill.skill}
))}
-
- {professionalSkills.map((skill) => (
-
- {skill}
+
+ {professionalSkills.data?.map((skill) => (
+
+ {skill.skill}
))}
diff --git a/src/contexts/FirebaseContext.tsx b/src/contexts/FirebaseContext.tsx
new file mode 100644
index 0000000..63665a4
--- /dev/null
+++ b/src/contexts/FirebaseContext.tsx
@@ -0,0 +1,21 @@
+import { initializeApp } from 'firebase/app';
+import { getFirestore } from 'firebase/firestore';
+import { ReactNode } from 'react';
+
+const firebaseConfig = {
+ apiKey: 'AIzaSyBhCceLOrVjvS6DJsY7rU0sbscU3FYqm3Y',
+ authDomain: 'resume-53741.firebaseapp.com',
+ projectId: 'resume-53741',
+ storageBucket: 'resume-53741.appspot.com',
+ messagingSenderId: '624958875679',
+ appId: '1:624958875679:web:7d6ccaf4e55c5597a64d2a',
+ measurementId: 'G-12M9EVHWWY'
+};
+
+const firebaseApp = initializeApp(firebaseConfig);
+
+export const db = getFirestore(firebaseApp);
+
+export const FirebaseContext = ({ children }: { children: ReactNode }) => {
+ return <>{children}>;
+};
diff --git a/src/contexts/QueryContext.tsx b/src/contexts/QueryContext.tsx
new file mode 100644
index 0000000..dbf67f1
--- /dev/null
+++ b/src/contexts/QueryContext.tsx
@@ -0,0 +1,14 @@
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
+import { ReactNode } from 'react';
+
+const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: Infinity } } });
+
+export const QueryContext = ({ children }: { children: ReactNode }) => {
+ return (
+
+
+ {children}
+
+ );
+};
diff --git a/src/main.tsx b/src/main.tsx
index 4eca6ba..cd9f6cd 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -1,28 +1,18 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
-import { initializeApp } from 'firebase/app';
import App from './App';
import './index.css';
-
-// Your web app's Firebase configuration
-// For Firebase JS SDK v7.20.0 and later, measurementId is optional
-const firebaseConfig = {
- apiKey: 'AIzaSyBhCceLOrVjvS6DJsY7rU0sbscU3FYqm3Y',
- authDomain: 'resume-53741.firebaseapp.com',
- projectId: 'resume-53741',
- storageBucket: 'resume-53741.appspot.com',
- messagingSenderId: '624958875679',
- appId: '1:624958875679:web:7d6ccaf4e55c5597a64d2a',
- measurementId: 'G-12M9EVHWWY'
-};
-
-const app = initializeApp(firebaseConfig);
-// const analytics = getAnalytics(app);
+import { FirebaseContext } from './contexts/FirebaseContext';
+import { QueryContext } from './contexts/QueryContext';
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
ReactDOM.createRoot(document.getElementById('root')!).render(
-
+
+
+
+
+
);
diff --git a/src/store/contact.ts b/src/store/contact.ts
new file mode 100644
index 0000000..fa26bac
--- /dev/null
+++ b/src/store/contact.ts
@@ -0,0 +1,21 @@
+import { useQuery } from '@tanstack/react-query';
+import { doc, getDoc } from 'firebase/firestore';
+
+import { contactInfo } from '~/store/store-backup';
+import { db } from '~/contexts/FirebaseContext';
+
+export interface ContactInfo {
+ email: string;
+ phone: string;
+ linkedin: string;
+ github: string;
+}
+
+export function useContactInfo() {
+ return useQuery
(['contact_info'], () =>
+ getDoc(doc(db, 'contact_info', '1')).then(
+ (r) => r.data() as ContactInfo,
+ () => contactInfo
+ )
+ );
+}
diff --git a/src/store/education.ts b/src/store/education.ts
new file mode 100644
index 0000000..43c5332
--- /dev/null
+++ b/src/store/education.ts
@@ -0,0 +1,21 @@
+import { useQuery } from '@tanstack/react-query';
+import { collection, getDocs } from 'firebase/firestore';
+
+import { education } from '~/store/store-backup';
+import { db } from '~/contexts/FirebaseContext';
+
+export interface EducationType {
+ title: string;
+ subTitle: string;
+ school: string;
+ duration: string;
+}
+
+export function useEducation() {
+ return useQuery(['education'], () =>
+ getDocs(collection(db, 'education')).then(
+ (r) => r.docs.map((doc) => doc.data()) as EducationType[],
+ () => education
+ )
+ );
+}
diff --git a/src/store/jobs.ts b/src/store/jobs.ts
new file mode 100644
index 0000000..f47a20e
--- /dev/null
+++ b/src/store/jobs.ts
@@ -0,0 +1,22 @@
+import { useQuery } from '@tanstack/react-query';
+import { collection, getDocs } from 'firebase/firestore';
+
+import { jobs } from '~/store/store-backup';
+import { db } from '~/contexts/FirebaseContext';
+
+export interface JobType {
+ title: string;
+ company: string;
+ duration: string;
+ description: string;
+ responsibilities: string[];
+}
+
+export function useJobs() {
+ return useQuery(['jobs'], () =>
+ getDocs(collection(db, 'jobs')).then(
+ (r) => r.docs.map((doc) => doc.data()) as JobType[],
+ () => jobs
+ )
+ );
+}
diff --git a/src/store/skills.ts b/src/store/skills.ts
new file mode 100644
index 0000000..95f1db2
--- /dev/null
+++ b/src/store/skills.ts
@@ -0,0 +1,24 @@
+import { useQuery } from '@tanstack/react-query';
+import { collection, getDocs } from 'firebase/firestore';
+
+import { db } from '~/contexts/FirebaseContext';
+
+export interface SkillType {
+ skill: string;
+}
+
+export function useTechnicalSkills() {
+ return useQuery(['skills', 'technical'], () =>
+ getDocs(collection(db, 'skills_technical')).then(
+ (r) => r.docs.map((doc) => doc.data()) as SkillType[]
+ )
+ );
+}
+
+export function useLeadershipSkills() {
+ return useQuery(['skills', 'leadership'], () =>
+ getDocs(collection(db, 'skills_leadership')).then(
+ (r) => r.docs.map((doc) => doc.data()) as SkillType[]
+ )
+ );
+}
diff --git a/src/content.ts b/src/store/store-backup.ts
similarity index 62%
rename from src/content.ts
rename to src/store/store-backup.ts
index 30f2cb7..9a2b1cb 100644
--- a/src/content.ts
+++ b/src/store/store-backup.ts
@@ -1,45 +1,33 @@
-export interface EducationType {
- title: string;
- subTitle: string;
- school: string;
- duration: string;
-}
+import { ContactInfo } from './contact';
+import { EducationType } from './education';
+import { JobType } from './jobs';
-export const education: EducationType = {
- title: 'Bachelor of Computer Science',
- subTitle: 'Minor in Business Administration (CO-OP)',
- school: 'University of New Brunswick',
- duration: 'September, 2016 - December, 2020'
-};
-
-export const aboutText =
- 'Nathan is a passionate, driven, and highly ambitious worker who takes immense pride in the things he creates. He cares deeply about the end user experience, and is dedicated to providing humane and beautiful interfaces for everyday use.';
+export const education: EducationType[] = [
+ {
+ title: 'Bachelor of Computer Science',
+ subTitle: 'Minor in Business Administration (CO-OP)',
+ school: 'University of New Brunswick',
+ duration: 'September, 2016 - December, 2020'
+ }
+];
export const technicalSkills: string[] = [
'React',
'Typescript',
'UI/UX Design',
'CSS',
- 'Testing',
- 'API Development',
- 'Python'
+ 'API Design',
+ 'Logging and Monitoring'
];
export const professionalSkills: string[] = [
'Project Management',
- 'Fast Learning',
- 'Communication',
- 'Finding Solutions'
+ 'Independent Learning',
+ 'Clear Communication',
+ 'Customer-First Mindset',
+ 'Mentoring'
];
-export interface JobType {
- title: string;
- company: string;
- duration: string;
- description: string;
- responsibilities: string[];
-}
-
export const jobs: JobType[] = [
{
title: 'Frontend Developer',
@@ -70,14 +58,14 @@ export const jobs: JobType[] = [
description: `Nathan worked 5 terms during his formal education as part of his CO-OP designation. Each term taught him new skills such as:`,
responsibilities: [
"Performing automated QA on an app's frontend",
- 'Thorough understanding of SCRUM and agile methodologies'
+ 'Thorough understanding of SCRUM and agile methodologiess'
]
}
];
-export const contactInfo: Record = {
+export const contactInfo: ContactInfo = {
email: 'natemacd97@gmail.com',
phone: '(506) 471-3038',
linkedin: 'www.linkedin.com/in/nathanmacd',
- github: 'https://github.com/nmacdon3/Resume'
+ github: 'https://github.com/nmacdon3'
};