Merge branch 'master' of https://github.com/gjcampbell777/personal_site
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
name: Optimize images
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, master ]
|
||||
paths:
|
||||
- 'src/assets/**'
|
||||
- 'assets/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/assets/**'
|
||||
- 'assets/**'
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
optimize:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '18'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run image optimizer
|
||||
run: npm run optimize-images
|
||||
|
||||
- name: Upload optimized images
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: optimized-images
|
||||
path: public/images
|
||||
@@ -21,3 +21,8 @@
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# amplify
|
||||
.amplify
|
||||
amplify_outputs*
|
||||
amplifyconfiguration*
|
||||
|
||||
@@ -1,6 +1,39 @@
|
||||
# personal_site
|
||||
|
||||
This repository is a React-based personal website for Gregory Campbell.
|
||||
|
||||
Quick commands
|
||||
|
||||
```bash
|
||||
# install deps (use legacy peer deps if you hit TypeScript/react-scripts conflicts)
|
||||
npm install --legacy-peer-deps
|
||||
|
||||
# run dev server
|
||||
npm start
|
||||
|
||||
# build production
|
||||
npm run build
|
||||
|
||||
# optimize images (requires sharp)
|
||||
# install sharp (one-time):
|
||||
npm run optimize-images:install
|
||||
# then run optimization:
|
||||
npm run optimize-images
|
||||
```
|
||||
|
||||
Image optimization
|
||||
|
||||
This repo includes `scripts/optimize-images.js`, a small Node script that uses `sharp` to generate WebP resized variants for images found in `src/assets`. The script writes outputs to `public/images` by default.
|
||||
|
||||
If you prefer a different pipeline, consider using `imagemin` or a CI-integrated image optimization step.
|
||||
|
||||
Deployment / Amplify notes
|
||||
|
||||
- The project uses `package-lock.json` (npm) — ensure CI is configured to use `npm install` and not `yarn`.
|
||||
- If deploying via AWS Amplify and it errors with multiple lockfiles, ensure only one lockfile (package-lock.json) is present.
|
||||
[](https://app.netlify.com/projects/gregoryjcampbell/deploys)
|
||||
|
||||
This is the source code for my [Personal Website](https://gregoryjcampbell.com).
|
||||
This is the source code for my [Personal Website](https://gregoryjcampbell.netlify.app/).
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
version: 1
|
||||
applications:
|
||||
- frontend:
|
||||
phases:
|
||||
preBuild:
|
||||
commands:
|
||||
- npm install --legacy-peer-deps
|
||||
build:
|
||||
commands:
|
||||
- npm run build
|
||||
artifacts:
|
||||
baseDirectory: build
|
||||
files:
|
||||
- "**/*"
|
||||
cache:
|
||||
paths:
|
||||
- node_modules/**/
|
||||
@@ -0,0 +1,11 @@
|
||||
import { defineAuth } from '@aws-amplify/backend';
|
||||
|
||||
/**
|
||||
* Define and configure your auth resource
|
||||
* @see https://docs.amplify.aws/gen2/build-a-backend/auth
|
||||
*/
|
||||
export const auth = defineAuth({
|
||||
loginWith: {
|
||||
email: true,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { defineBackend } from '@aws-amplify/backend';
|
||||
import { auth } from './auth/resource';
|
||||
import { data } from './data/resource';
|
||||
|
||||
/**
|
||||
* @see https://docs.amplify.aws/react/build-a-backend/ to add storage, functions, and more
|
||||
*/
|
||||
defineBackend({
|
||||
auth,
|
||||
data,
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
import { type ClientSchema, a, defineData } from '@aws-amplify/backend';
|
||||
|
||||
/*== STEP 1 ===============================================================
|
||||
The section below creates a Todo database table with a "content" field. Try
|
||||
adding a new "isDone" field as a boolean. The authorization rule below
|
||||
specifies that any unauthenticated user can "create", "read", "update",
|
||||
and "delete" any "Todo" records.
|
||||
=========================================================================*/
|
||||
const schema = a.schema({
|
||||
Todo: a
|
||||
.model({
|
||||
content: a.string(),
|
||||
})
|
||||
.authorization((allow) => [allow.guest()]),
|
||||
});
|
||||
|
||||
export type Schema = ClientSchema<typeof schema>;
|
||||
|
||||
export const data = defineData({
|
||||
schema,
|
||||
authorizationModes: {
|
||||
defaultAuthorizationMode: 'identityPool',
|
||||
},
|
||||
});
|
||||
|
||||
/*== STEP 2 ===============================================================
|
||||
Go to your frontend source code. From your client-side code, generate a
|
||||
Data client to make CRUDL requests to your table. (THIS SNIPPET WILL ONLY
|
||||
WORK IN THE FRONTEND CODE FILE.)
|
||||
|
||||
Using JavaScript or Next.js React Server Components, Middleware, Server
|
||||
Actions or Pages Router? Review how to generate Data clients for those use
|
||||
cases: https://docs.amplify.aws/gen2/build-a-backend/data/connect-to-API/
|
||||
=========================================================================*/
|
||||
|
||||
/*
|
||||
"use client"
|
||||
import { generateClient } from "aws-amplify/data";
|
||||
import type { Schema } from "@/amplify/data/resource";
|
||||
|
||||
const client = generateClient<Schema>() // use this Data client for CRUDL requests
|
||||
*/
|
||||
|
||||
/*== STEP 3 ===============================================================
|
||||
Fetch records from the database and use them in your frontend component.
|
||||
(THIS SNIPPET WILL ONLY WORK IN THE FRONTEND CODE FILE.)
|
||||
=========================================================================*/
|
||||
|
||||
/* For example, in a React component, you can use this snippet in your
|
||||
function's RETURN statement */
|
||||
// const { data: todos } = await client.models.Todo.list()
|
||||
|
||||
// return <ul>{todos.map(todo => <li key={todo.id}>{todo.content}</li>)}</ul>
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2022",
|
||||
"module": "es2022",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
"$amplify/*": [
|
||||
"../.amplify/generated/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+31752
-1475
File diff suppressed because it is too large
Load Diff
@@ -5,10 +5,13 @@
|
||||
"repository": "https://github.com/gjcampbell777/personal_site.git",
|
||||
"author": "Gregory Campbell <gjcampbell777@gmail.com>",
|
||||
"license": "MIT",
|
||||
"homepage": ".",
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.11.1",
|
||||
"@testing-library/react": "^11.2.5",
|
||||
"@testing-library/user-event": "^12.0.11",
|
||||
"ajv": "^8.12.0",
|
||||
"aws-amplify": "^6.17.0",
|
||||
"bootstrap": "^5.0.0",
|
||||
"immer": "9.0.6",
|
||||
"ini": "2.0.0",
|
||||
@@ -18,6 +21,8 @@
|
||||
"react": "^17.0.1",
|
||||
"react-bootstrap": "^1.2.2",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-helmet-async": "^1.3.0",
|
||||
"react-is": "^19.2.7",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "^5.0.1",
|
||||
"styled-components": "^5.1.1"
|
||||
@@ -42,5 +47,15 @@
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-amplify/backend": "^1.23.0",
|
||||
"@aws-amplify/backend-cli": "^1.8.3",
|
||||
"aws-cdk-lib": "^2.244.0",
|
||||
"constructs": "^10.6.0",
|
||||
"esbuild": "^0.28.0",
|
||||
"sharp": "^0.34.5",
|
||||
"tsx": "^4.22.4",
|
||||
"typescript": "^4.9.5"
|
||||
}
|
||||
}
|
||||
|
||||
+3
-12
@@ -2,34 +2,25 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<link rel="icon" href="favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="The Personal Website of Gregory Campbell"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<link rel="apple-touch-icon" href="logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
|
||||
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>Gregory J Campbell</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"version": 1,
|
||||
"skills": {
|
||||
"cavecrew": {
|
||||
"source": "JuliusBrussee/caveman",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/cavecrew/SKILL.md",
|
||||
"computedHash": "505d836228d1c5e14834ff5d62aad72390c7d27f79c6aa7f9a7a55ed6606d6a2"
|
||||
},
|
||||
"caveman": {
|
||||
"source": "JuliusBrussee/caveman",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/caveman/SKILL.md",
|
||||
"computedHash": "dfbf85749fd474feeb0bbe60c779795ecd5dbec0083299b56e68916bc3ddd8c9"
|
||||
},
|
||||
"caveman-commit": {
|
||||
"source": "JuliusBrussee/caveman",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/caveman-commit/SKILL.md",
|
||||
"computedHash": "f456ea0564875e46858890ac39ee701ecb9c601c72a2da1e6ce6bd5f9fc5d817"
|
||||
},
|
||||
"caveman-compress": {
|
||||
"source": "JuliusBrussee/caveman",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/caveman-compress/SKILL.md",
|
||||
"computedHash": "b8cbfec00b620f0944960a9bb4072f262cf816c1d3c82e6dbded35c30b4c5d0b"
|
||||
},
|
||||
"caveman-help": {
|
||||
"source": "JuliusBrussee/caveman",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/caveman-help/SKILL.md",
|
||||
"computedHash": "2c21437427e98df1eacabaa0b055b5256a308b2191feea3ca2df6a9418e76cb1"
|
||||
},
|
||||
"caveman-review": {
|
||||
"source": "JuliusBrussee/caveman",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/caveman-review/SKILL.md",
|
||||
"computedHash": "fb7214a1c5793bae6ba8b1be4329e2e6f40dbec6dd911dfb335ad29f09c316a1"
|
||||
},
|
||||
"caveman-stats": {
|
||||
"source": "JuliusBrussee/caveman",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/caveman-stats/SKILL.md",
|
||||
"computedHash": "47ce2de3d6cb39a75047b5c962e4eb3da15594e7397c94103e9a104d42626553"
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
-20
@@ -1,22 +1,20 @@
|
||||
import React from 'react'
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
|
||||
export const NoMatch = () => (
|
||||
<div>
|
||||
<head>
|
||||
<title>404 ERROR</title>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>404 ERROR</h1>
|
||||
export const NoMatch = () => {
|
||||
return (
|
||||
<main className="notfound-page">
|
||||
<Helmet>
|
||||
<title>404 Error</title>
|
||||
</Helmet>
|
||||
|
||||
<aside>Hey, you shouldn't be here!</aside>
|
||||
</header>
|
||||
|
||||
<br/>
|
||||
|
||||
<h2>Don't know how you got here but you should probably turn around and head back.</h2>
|
||||
|
||||
<p>There's nothing here!</p>
|
||||
</body>
|
||||
</div>
|
||||
)
|
||||
<header>
|
||||
<h1>404 ERROR</h1>
|
||||
<p>Hey, you shouldn't be here!</p>
|
||||
</header>
|
||||
<section>
|
||||
<p>Don't know how you got here, but you should probably head back.</p>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
+20
-23
@@ -1,5 +1,6 @@
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
|
||||
import { HelmetProvider } from 'react-helmet-async';
|
||||
import { Home } from './Home';
|
||||
import { Resume } from './Resume';
|
||||
import { Projects } from './Projects';
|
||||
@@ -10,27 +11,23 @@ import { NavigationBar } from './components/NavigationBar';
|
||||
import { Jumbotron } from './components/Jumbotron';
|
||||
import Footer from './components/Footer';
|
||||
|
||||
class App extends Component {
|
||||
render() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Router>
|
||||
<NavigationBar />
|
||||
<Jumbotron />
|
||||
<Layout>
|
||||
<Switch>
|
||||
<Route exact path="/" component={Home} />
|
||||
<Route path="/Resume" component={Resume} />
|
||||
<Route path="/Projects" component={Projects} />
|
||||
<Route path="/Contact" component={Contact} />
|
||||
<Route component={NoMatch} />
|
||||
</Switch>
|
||||
</Layout>
|
||||
<Footer />
|
||||
</Router>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
const App = () => (
|
||||
<HelmetProvider>
|
||||
<Router>
|
||||
<NavigationBar />
|
||||
<Jumbotron />
|
||||
<Layout>
|
||||
<Switch>
|
||||
<Route exact path="/" component={Home} />
|
||||
<Route path="/Resume" component={Resume} />
|
||||
<Route path="/Projects" component={Projects} />
|
||||
<Route path="/Contact" component={Contact} />
|
||||
<Route component={NoMatch} />
|
||||
</Switch>
|
||||
</Layout>
|
||||
<Footer />
|
||||
</Router>
|
||||
</HelmetProvider>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
||||
+24
-42
@@ -1,45 +1,27 @@
|
||||
import React from 'react'
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
|
||||
export const Contact = () => (
|
||||
<div>
|
||||
<head>
|
||||
<title>Contact Info</title>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>How to Reach Me</h1>
|
||||
export const Contact = () => {
|
||||
return (
|
||||
<main className="contact-page">
|
||||
<Helmet>
|
||||
<title>Contact Gregory Campbell</title>
|
||||
<meta name="description" content="Contact details for Gregory Campbell." />
|
||||
</Helmet>
|
||||
|
||||
<aside>Give me a shout!</aside>
|
||||
</header>
|
||||
<header>
|
||||
<h1>How to Reach Me</h1>
|
||||
<p>Give me a shout!</p>
|
||||
</header>
|
||||
|
||||
<br/>
|
||||
|
||||
<h2>Contact Info:</h2>
|
||||
|
||||
<br/>
|
||||
|
||||
<h2>Email:
|
||||
<a href="mailto:gjcampbell777@gmail.com">gjcampbell777@gmail.com </a>
|
||||
</h2>
|
||||
|
||||
<h2>Cell #:
|
||||
<a href="tel:6477797951">(647)-779-7951</a>
|
||||
</h2>
|
||||
|
||||
<h2>Twitter:
|
||||
<a href="https://twitter.com/Gregory_Soup">@Gregory_Soup</a>
|
||||
</h2>
|
||||
|
||||
<h2>LinkedIn:
|
||||
<a href="https://www.linkedin.com/in/gregoryjcampbell/">Gregory J Campbell</a>
|
||||
</h2>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<hr />
|
||||
|
||||
</body>
|
||||
</div>
|
||||
)
|
||||
<section>
|
||||
<h2>Contact Info</h2>
|
||||
<ul className="contact-list">
|
||||
<li>Email: <a href="mailto:gjcampbell777@gmail.com">gjcampbell777@gmail.com</a></li>
|
||||
<li>Phone: <a href="tel:6477797951">(647) 779-7951</a></li>
|
||||
<li>LinkedIn: <a href="https://www.linkedin.com/in/gregoryjcampbell/" target="_blank" rel="noreferrer noopener">Gregory J Campbell</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
+33
-46
@@ -1,50 +1,37 @@
|
||||
import React from 'react'
|
||||
import Me from './assets/Me.jpeg'
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
import meImage from './assets/Me.jpeg';
|
||||
|
||||
export const Home = () => (
|
||||
<div>
|
||||
<head>
|
||||
<title>The Works of Gregory Campbell</title>
|
||||
</head>
|
||||
<body>
|
||||
export const Home = () => {
|
||||
return (
|
||||
<main className="home-page">
|
||||
<Helmet>
|
||||
<title>The Works of Gregory Campbell</title>
|
||||
<meta name="description" content="Personal website and portfolio of Gregory Campbell." />
|
||||
</Helmet>
|
||||
|
||||
<h2>Welcome to the personal website of Gregory Campbell</h2>
|
||||
<section>
|
||||
<h2>Welcome to the personal website of Gregory Campbell</h2>
|
||||
<p>I hope you enjoy everything it has to offer.</p>
|
||||
</section>
|
||||
|
||||
<p>I hope you enjoy everything it has to offer</p>
|
||||
<section>
|
||||
<h3>Finished projects viewable on this site</h3>
|
||||
<ul>
|
||||
<li>This website is clean, easy to read, written in React, and adapts well to mobile.</li>
|
||||
<li>I also added a HTML and downloadable PDF version of my resume on the Resume page.</li>
|
||||
<li>Check out the many projects on the Projects tab.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<h3>Finished Projects Viewable on this site:</h3>
|
||||
|
||||
<p></p>
|
||||
|
||||
<ul>
|
||||
<li>This website. It might not seem like much but it's clean, easy to read, is written in reactjs and adapts quite well to mobile.</li>
|
||||
</ul>
|
||||
|
||||
<p></p>
|
||||
|
||||
<p></p>
|
||||
|
||||
<ul>
|
||||
<li>I also added a html and downloadable .pdf version of my resume in the <a href="https://gregoryjcampbell.com/Resume">Resume</a> page, feel free to give it a look!</li>
|
||||
</ul>
|
||||
|
||||
<p></p>
|
||||
|
||||
<ul>
|
||||
<li>The many projects that I've mosted in the <a href="https://gregoryjcampbell.com/Projects">Projects</a> tab, go check it out!</li>
|
||||
</ul>
|
||||
|
||||
<p></p>
|
||||
|
||||
<blockquote></blockquote>
|
||||
|
||||
<br/>
|
||||
|
||||
<img src={Me} alt="It's Me!" className="homeimage"/>
|
||||
|
||||
<br/>
|
||||
|
||||
<hr />
|
||||
</body>
|
||||
</div>
|
||||
)
|
||||
<section>
|
||||
<img
|
||||
src={meImage}
|
||||
alt="Portrait of Gregory Campbell"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
+153
-236
@@ -1,236 +1,153 @@
|
||||
import React from 'react'
|
||||
import ShowingOff from './assets/ShowingOff.jpg'
|
||||
import MJB from './assets/MJB.jpg'
|
||||
import GC from './assets/GunControl.png'
|
||||
import Palish from './assets/Palish.png'
|
||||
import SRS from './assets/SRS.png'
|
||||
import WH from './assets/W&H.png'
|
||||
import StateMachine from './assets/StateMachine.png'
|
||||
import Skin2 from './assets/skin2.png'
|
||||
import HSV from './assets/HSV.png'
|
||||
import EulerCycle from './assets/EulerCycle.png'
|
||||
import EulerExample from './assets/EulerExample.png'
|
||||
import Hangman from './assets/Hangman.png'
|
||||
import KngihtsTour from './assets/KnightsTour.png'
|
||||
import TextAnalyzer from './assets/TextAnalyzer.png'
|
||||
import AlgorithmComparison from './assets/AlgorithmComparison.png'
|
||||
|
||||
export const Projects = () => (
|
||||
<div>
|
||||
<head>
|
||||
<title>The Projects of Gregory Campbell</title>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>My Projects</h1>
|
||||
|
||||
<aside>Look at what I've created!</aside>
|
||||
</header>
|
||||
|
||||
<br/>
|
||||
|
||||
<h2>This page contains the projects I've done over the past, present and future.</h2>
|
||||
|
||||
<br/>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h4>If you want to see what I'm currently working on you can see all my updates
|
||||
<a href="https://twitter.com/Gregory_Soup">Here</a>
|
||||
</h4>
|
||||
|
||||
<hr/>
|
||||
<h3> <a href="https://github.com/gjcampbell777/python-state-machine">Python State Machine</a> </h3>
|
||||
|
||||
<br/>
|
||||
|
||||
<p>Recently while going through Godot documentation I came across a tutorial for building your own state machine with Python.
|
||||
I've always been interested in making a stand alone state machine and state machines are a big part of game developement
|
||||
so I figured it would be a good idea for me to check it out. After I finished the tutorial I decided to continue working on
|
||||
and fleshing out this state machine used to emulate a locked/unlocked device.
|
||||
You can check out my code and mess around with the state machine yourself <a href="https://github.com/gjcampbell777/python-state-machine">Here</a>.
|
||||
</p>
|
||||
|
||||
<img src={StateMachine} alt="StateMachine"/>
|
||||
|
||||
<br/><br/>
|
||||
|
||||
<h3> <a href="https://github.com/gjcampbell777/python-photo-skin-detection">Python Skin Detection Algorithms</a> </h3>
|
||||
|
||||
<br/>
|
||||
|
||||
<p>In an image processing course I took in university we were tasked with replicating multiple skin detection algorithms through a python script.
|
||||
I enjoyed working on that assignment and was fascinated by how well (or not so well) a program is able to detect things that we as humans can figure out easily.
|
||||
It's one of the few assignments I worked on in university that I can still remember quite well so I figured I'd find it wherever I had saved it
|
||||
and clean it up as well as make sure it can run with python3.
|
||||
You can check out my code and run your own photos through the script by checking out the repo <a href="https://github.com/gjcampbell777/python-photo-skin-detection">Here</a>.
|
||||
</p>
|
||||
|
||||
<img src={Skin2} alt="Before"/>
|
||||
|
||||
<p>Before going through the algorithm</p>
|
||||
|
||||
<br/>
|
||||
|
||||
<img src={HSV} alt="After"/>
|
||||
|
||||
<p>After going through the HSV algorithm</p>
|
||||
|
||||
<br/>
|
||||
|
||||
<h3> <a href="https://github.com/gjcampbell777/euler-checking-algorithm">Euler Checking Algorithm</a> </h3>
|
||||
|
||||
<br/>
|
||||
|
||||
<p>In a course I took in university we were tasked checking if a graph was Eulerian or not.
|
||||
Much like the Python Skin Detection assignment this was also an assignment I could still remember
|
||||
from those days and wanted to re-explore and improve upon.
|
||||
This C algorithm takes the text file euler-graphs.txt and runs through the input to determine if its contents contain a Euler graph.
|
||||
The algorithm checks the initial line of a graph to determine the length of the graph and
|
||||
uses that information to know how many lines it must go through to gather all the info of the graph.
|
||||
It will repeat this process until it reaches EOF.
|
||||
You can check out my code by checking out the repo <a href="https://github.com/gjcampbell777/euler-checking-algorithm">Here</a>.
|
||||
</p>
|
||||
|
||||
<br/>
|
||||
|
||||
<img src={EulerExample} alt="EulerExample"/>
|
||||
|
||||
<p>Example of a graph from euler-graphs.txt</p>
|
||||
|
||||
<br/>
|
||||
|
||||
<img src={EulerCycle} alt="EulerCycle"/>
|
||||
|
||||
<p>Output from running the algorithm with euler-graphs.txt</p>
|
||||
|
||||
<br/>
|
||||
|
||||
<h3> <a href="https://github.com/gjcampbell777/fortran-hangman">Fortran Hangman</a> </h3>
|
||||
|
||||
<br/>
|
||||
|
||||
<p>In a legacy programming course I took in university we were tasked with updating a fortran77 hangman program to fortran95
|
||||
as well as improving upon the code itself.
|
||||
Much like some of the other projects above this was also an assignment I could still remember
|
||||
from those days and wanted to re-explore and improve upon.
|
||||
Run the executable to play a simple game of hangman.
|
||||
You can even use the makefile I added to generate an executable of the fortran77 version.
|
||||
You can check out my code for this project <a href="https://github.com/gjcampbell777/fortran-hangman">Here</a>.
|
||||
</p>
|
||||
|
||||
<br/>
|
||||
|
||||
<img src={Hangman} alt="Hangman"/>
|
||||
<p>A very quick game of hangman</p>
|
||||
|
||||
<br/>
|
||||
|
||||
<h3> <a href="https://github.com/gjcampbell777/ada-knights-tour">Ada Kngihts Tour</a> </h3>
|
||||
|
||||
<br/>
|
||||
|
||||
<p>In a legacy programming course I took in university we were tasked with creating a program that could solve the knights tour problem using Ada.
|
||||
Much like some of the other projects above this was also an assignment I could still remember
|
||||
from those days and wanted to re-explore and improve upon.
|
||||
Run the executable and follow the prompts to have the program solve the problem for you.
|
||||
However you should try to not make the chessboard size bigger than 7 if you don't have time to spare.
|
||||
You can check out my code for this project <a href="https://github.com/gjcampbell777/ada-knights-tour">Here</a>.
|
||||
</p>
|
||||
|
||||
<br/>
|
||||
|
||||
<img src={KngihtsTour} alt="KnightsTour"/>
|
||||
|
||||
<p>Knights tour output for a 5x5 chessboard starting at position (1,1)</p>
|
||||
|
||||
<br/>
|
||||
|
||||
<h3> <a href="https://github.com/gjcampbell777/cobol-text-analyzer">Cobol Text Analyzer</a> </h3>
|
||||
|
||||
<br/>
|
||||
|
||||
<p>In a legacy programming course I took in university we were tasked with improving upon a cobol script that
|
||||
was full of dated coding practices.
|
||||
Much like some of the other projects above this was also an assignment I could still remember
|
||||
from those days and wanted to re-explore and improve upon.
|
||||
Run the executable and tell it which textfile you wish to have analyzed to see the text stats.
|
||||
You can even use the makefile I added to generate an executable of the dated script.
|
||||
However the text file that script takes is hard coded so you will have to change some things around if you want to analyze different text with it.
|
||||
You can check out my code for this project <a href="https://github.com/gjcampbell777/cobol-text-analyzer">Here</a>.
|
||||
</p>
|
||||
|
||||
<br/>
|
||||
|
||||
<img src={TextAnalyzer} alt="TextAnalyzer"/>
|
||||
|
||||
<p>Analyzing some text from Star Wars</p>
|
||||
|
||||
<br/>
|
||||
|
||||
<h3> <a href="https://github.com/gjcampbell777/legacy-algorithm-comparison">Legacy Code Algorithm Comparison</a> </h3>
|
||||
|
||||
<br/>
|
||||
|
||||
<p>In a legacy programming course I took in university we were tasked with comparing the speeds of C, Fortran and Ada using the KMP search algorithm.
|
||||
Much like some of the other projects above this was also an assignment I could still remember
|
||||
from those days and wanted to re-explore and improve upon.
|
||||
Run the executable of your choice to see the speed of that coding language.
|
||||
Using the makefile I created generates executables for each programming language.
|
||||
You can check out my code for this project <a href="https://github.com/gjcampbell777/legacy-algorithm-comparison">Here</a>.
|
||||
</p>
|
||||
|
||||
<br/>
|
||||
|
||||
<img src={AlgorithmComparison} alt="AlgorithmComparison"/>
|
||||
|
||||
<p>Seeing how quickly C, Fortran and Ada are all able to find "Q" in a block of text</p>
|
||||
|
||||
<br/>
|
||||
|
||||
<h3> <a href="https://gregorysoup.itch.io">My Many Games</a> </h3>
|
||||
|
||||
<br/>
|
||||
|
||||
<p>Over the course of the last decade I've made a handful of games on my own.
|
||||
I've always been interested in game developement and I use this as a way to experiment and
|
||||
create games I would like to play myself. The fact that I've made all these games on my own allows me
|
||||
to go at my own pace as well as learn every aspect there is to this type of development while still being able to put my own charm into each game I make.
|
||||
I've worked on a fast paced endless runner 3D platformer, a 2D turn based combat puzzle and many game jam games.
|
||||
I continue to explore and create new game ideas as they pop into my head, so be sure to be on the look out for what I make next.
|
||||
Any game I've worked on has either been created using Unity or Godot which both utilize their own proprietary coding languages.
|
||||
</p>
|
||||
|
||||
<br/>
|
||||
|
||||
<img src={ShowingOff} alt="Presenting my game at a toronto indie game dev meet up"/>
|
||||
|
||||
<br/>
|
||||
|
||||
<br/>
|
||||
|
||||
<img src={MJB} alt="MJB"/>
|
||||
|
||||
<br/>
|
||||
|
||||
<img src={WH} alt="WH"/>
|
||||
|
||||
<br/>
|
||||
|
||||
<img src={SRS} alt="SRS"/>
|
||||
|
||||
<br/>
|
||||
|
||||
<img src={Palish} alt="Palish"/>
|
||||
|
||||
<br/>
|
||||
|
||||
<img src={GC} alt="GC"/>
|
||||
|
||||
<br/>
|
||||
|
||||
<hr />
|
||||
|
||||
</body>
|
||||
</div>
|
||||
)
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
import ProjectCard from './components/ProjectCard';
|
||||
|
||||
// Import all project images
|
||||
import StateMachine from './assets/StateMachine.png';
|
||||
import HSV from './assets/HSV.png';
|
||||
import skin2 from './assets/skin2.png';
|
||||
import EulerExample from './assets/EulerExample.png';
|
||||
import EulerCycle from './assets/EulerCycle.png';
|
||||
import Hangman from './assets/Hangman.png';
|
||||
import KnightsTour from './assets/KnightsTour.png';
|
||||
import TextAnalyzer from './assets/TextAnalyzer.png';
|
||||
import AlgorithmComparison from './assets/AlgorithmComparison.png';
|
||||
import ShowingOff from './assets/ShowingOff.jpg';
|
||||
import MJB from './assets/MJB.jpg';
|
||||
import GunControl from './assets/GunControl.png';
|
||||
import Palish from './assets/Palish.png';
|
||||
import WH from './assets/W&H.png';
|
||||
import SRS from './assets/SRS.png';
|
||||
|
||||
|
||||
// Map imgName to imported images
|
||||
const imageMap = {
|
||||
StateMachine,
|
||||
HSV,
|
||||
skin2,
|
||||
EulerExample,
|
||||
EulerCycle,
|
||||
Hangman,
|
||||
KnightsTour,
|
||||
TextAnalyzer,
|
||||
AlgorithmComparison,
|
||||
ShowingOff,
|
||||
MJB,
|
||||
GunControl,
|
||||
Palish,
|
||||
WH,
|
||||
SRS
|
||||
};
|
||||
|
||||
const projects = [
|
||||
{
|
||||
title: 'Python State Machine',
|
||||
link: 'https://github.com/gjcampbell777/python-state-machine',
|
||||
description: 'A Python state machine used to emulate a locked/unlocked device.',
|
||||
imgName: 'StateMachine',
|
||||
imgAlt: 'Python state machine demo'
|
||||
},
|
||||
{
|
||||
title: 'Python Skin Detection Algorithms',
|
||||
link: 'https://github.com/gjcampbell777/python-photo-skin-detection',
|
||||
description: 'Replicated multiple skin detection algorithms in Python and modernized them for Python 3.',
|
||||
imgName: 'HSV',
|
||||
imgAlt: 'Skin detection image after',
|
||||
},
|
||||
{
|
||||
imgName: 'skin2',
|
||||
imgAlt: 'Skin detection image before',
|
||||
},
|
||||
{
|
||||
title: 'Euler Checking Algorithm',
|
||||
link: 'https://github.com/gjcampbell777/euler-checking-algorithm',
|
||||
description: 'C program to check if graphs are Eulerian.',
|
||||
imgName: 'EulerExample',
|
||||
imgAlt: 'Euler example'
|
||||
},
|
||||
{
|
||||
imgName: 'EulerCycle',
|
||||
imgAlt: 'Euler cycle output',
|
||||
},
|
||||
{
|
||||
title: 'Fortran Hangman',
|
||||
link: 'https://github.com/gjcampbell777/fortran-hangman',
|
||||
description: 'Updated a Fortran 77 hangman game to Fortran 95 and improved the code.',
|
||||
imgName: 'Hangman',
|
||||
imgAlt: 'Hangman game'
|
||||
},
|
||||
{
|
||||
title: "Ada Knights Tour",
|
||||
link: 'https://github.com/gjcampbell777/ada-knights-tour',
|
||||
description: "Solves the knight's tour problem in Ada.",
|
||||
imgName: 'KnightsTour',
|
||||
imgAlt: 'Knights tour output'
|
||||
},
|
||||
{
|
||||
title: 'COBOL Text Analyzer',
|
||||
link: 'https://github.com/gjcampbell777/cobol-text-analyzer',
|
||||
description: 'Modernized COBOL text analyzer to report text statistics.',
|
||||
imgName: 'TextAnalyzer',
|
||||
imgAlt: 'Text analyzer output'
|
||||
},
|
||||
{
|
||||
title: 'Legacy Algorithm Comparison',
|
||||
link: 'https://github.com/gjcampbell777/legacy-algorithm-comparison',
|
||||
description: 'Compare KMP implementations across C, Fortran, and Ada.',
|
||||
imgName: 'AlgorithmComparison',
|
||||
imgAlt: 'Algorithm comparison charts'
|
||||
},
|
||||
{
|
||||
title: 'My Many Games',
|
||||
description: 'A selection of games made with Unity and Godot.',
|
||||
imgName: 'ShowingOff',
|
||||
imgAlt: 'Game showcase'
|
||||
},
|
||||
{
|
||||
imgName: 'MJB',
|
||||
imgAlt: 'Magical Jumping Bean'
|
||||
},
|
||||
{
|
||||
imgName: 'GunControl',
|
||||
imgAlt: 'Gun Control'
|
||||
},
|
||||
{
|
||||
imgName: 'Palish',
|
||||
imgAlt: 'Palish'
|
||||
},
|
||||
{
|
||||
imgName: 'WH',
|
||||
imgAlt: 'Wally And Harv Escape the Fireball and Brick Factory'
|
||||
},
|
||||
{
|
||||
imgName: 'SRS',
|
||||
imgAlt: 'Super Robo Squad'
|
||||
},
|
||||
];
|
||||
|
||||
export const Projects = () => {
|
||||
|
||||
return (
|
||||
<main className="projects-page">
|
||||
<Helmet>
|
||||
<title>The Projects of Gregory Campbell</title>
|
||||
<meta name="description" content="Collection of projects by Gregory Campbell." />
|
||||
</Helmet>
|
||||
<header>
|
||||
<h1>My Projects</h1>
|
||||
<aside>Look at what I've created!</aside>
|
||||
</header>
|
||||
|
||||
{projects.map((p) => (
|
||||
<ProjectCard
|
||||
key={p.title}
|
||||
title={p.title}
|
||||
description={p.description}
|
||||
imgSrc={imageMap[p.imgName]}
|
||||
imgAlt={p.imgAlt}
|
||||
link={p.link}
|
||||
/>
|
||||
))}
|
||||
</main>
|
||||
);
|
||||
};
|
||||
+78
-93
@@ -1,103 +1,88 @@
|
||||
import React from 'react'
|
||||
import pdf from './assets/Gregory Campbell Resume.pdf'
|
||||
import pic from './assets/Resume.png'
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
import pdf from './assets/Gregory Campbell Resume.pdf';
|
||||
import resumeImage from './assets/Resume.png';
|
||||
|
||||
export const Resume = () => (
|
||||
<div>
|
||||
<head>
|
||||
<title>The Resume of Gregory Campbell</title>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>My Resume</h1>
|
||||
export const Resume = () => {
|
||||
return (
|
||||
<main className="resume-page">
|
||||
<Helmet>
|
||||
<title>The Resume of Gregory Campbell</title>
|
||||
<meta name="description" content="Resume and professional experience of Gregory Campbell." />
|
||||
</Helmet>
|
||||
|
||||
<aside>Check out everything I have accomplished in my career.</aside>
|
||||
</header>
|
||||
<header>
|
||||
<h1>My Resume</h1>
|
||||
<p>Check out everything I have accomplished in my career.</p>
|
||||
</header>
|
||||
|
||||
<br/>
|
||||
<section>
|
||||
<h2>Downloadable PDF</h2>
|
||||
<p>
|
||||
You can view and download a PDF version of my resume{' '}
|
||||
<a href={pdf} target="_blank" rel="noreferrer noopener">here</a>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<h2>Downloadable PDF</h2>
|
||||
<section>
|
||||
<h2>Written Resume</h2>
|
||||
<img
|
||||
src={resumeImage}
|
||||
alt="Resume preview"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<p>You can view and download a .pdf verison of my resume <a href={pdf}>Here</a></p>
|
||||
<section>
|
||||
<h2>Gregory Campbell</h2>
|
||||
<p>
|
||||
<a href="tel:6477797951">(647) 779-7951</a> ·{' '}
|
||||
<a href="mailto:gjcampbell777@gmail.com">gjcampbell777@gmail.com</a>
|
||||
</p>
|
||||
|
||||
<br/>
|
||||
<h3>Profile</h3>
|
||||
<p>Highly motivated and team-focused software developer with 4+ years of experience developing code and creating test environments and infrastructure.</p>
|
||||
|
||||
<h2><u>Resume</u></h2>
|
||||
<h3>Education</h3>
|
||||
<p>
|
||||
<strong>Bachelor of Computing, Computer Science (Co-op)</strong><br />
|
||||
<em>University of Guelph – Guelph, Ontario</em><br />
|
||||
Completed April 2017
|
||||
</p>
|
||||
|
||||
<br/>
|
||||
<h3>Work Experience</h3>
|
||||
<article>
|
||||
<h4>Software Developer</h4>
|
||||
<p><em>Clear Blue Technologies – North York, Ontario</em> · Aug 2017 - Present</p>
|
||||
<ul>
|
||||
<li>Added features and fixed issues with various controllers using the functional programming language Elixir.</li>
|
||||
<li>Built automated black box testing devices and infrastructure with controller code, Raspberry Pi, and programmable power supplies.</li>
|
||||
<li>Developed automated black box front end testing across Chrome, Firefox, and Safari using TestCafe.</li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<img src={pic} alt="Resume"/>
|
||||
<article>
|
||||
<h4>Software Test Specialist</h4>
|
||||
<p><em>IBM (AppScan Source team) – Markham, Ontario</em> · Jun 2014 - Aug 2016</p>
|
||||
<ul>
|
||||
<li>Created a test tool in HTML and JavaScript to help developers efficiently test builds using subsets of smoke tests.</li>
|
||||
<li>Presented security findings to stakeholders, architects, and developers.</li>
|
||||
<li>Integrated the Juliet test suite into AppScan Source test automation using Java.</li>
|
||||
<li>Developed, debugged, executed, and maintained Bash testing scripts.</li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<br/>
|
||||
|
||||
<h2><u>Written Resume</u></h2>
|
||||
|
||||
<br/>
|
||||
|
||||
<h2>Gregory Campbell</h2>
|
||||
|
||||
<p align="right">
|
||||
<a href="tel:6477797951">(647)-779-7951</a><br />
|
||||
<b><a href="mailto:gjcampbell777@gmail.com">gjcampbell777@gmail.com </a></b>
|
||||
</p>
|
||||
|
||||
<h3><u>Profile:</u></h3>
|
||||
|
||||
<p>Highly motivated and team-focused software developer with 4+ years of experience developing code and creating test environments and infrastructure.</p>
|
||||
|
||||
<br/>
|
||||
|
||||
<h3><u>Education:</u></h3>
|
||||
|
||||
<br/>
|
||||
|
||||
<p><b>Bachelor of Computing, Computer Science (Co-op)</b><br />
|
||||
<i>University of Guelph – Guelph, Ontario</i> <span>Completed April 2017 </span></p>
|
||||
|
||||
<br/>
|
||||
|
||||
<h3><u>Work Experience</u></h3>
|
||||
|
||||
<p><b>Software Developer,</b><br />
|
||||
<i>Clear Blue Technologies – North York, Ontario</i> <span>Aug 2017 - Present </span></p>
|
||||
|
||||
<ul>
|
||||
<li>Adding features and fixing issues with various controllers using the functional programming language Elixir.</li>
|
||||
<li>Built multiple varieties of automated black box testing devices and accompanying infrastructure with the controller, a raspberry pi, a programmable power supply and elixir code from scratch.</li>
|
||||
<li>Developed automated black box front end testing infrastructure of the company web service across Chrome, Firefox and Safari using the node.js automation tool "Testcafe".</li>
|
||||
</ul>
|
||||
|
||||
<p><b>Software Test Specialist,</b><br />
|
||||
<i>IBM (Appscan Source team) – Markham, Ontario</i> <span>Jun 2014 - Aug 2016 </span></p>
|
||||
|
||||
<ul>
|
||||
<li>Created a test tool in HTML and Javascript with API usage for developers to more efficiently test builds using subsets of smoke tests.</li>
|
||||
<li>Multiple presentations to stakeholders, architects and developers on security vulnerabilities, demoed security programs. </li>
|
||||
<li>Integrated Juliet test suite into the Appscan Source test automation system using Java.</li>
|
||||
<li>Developed, debugged, executed and maintained bash testing scripts.</li>
|
||||
</ul>
|
||||
|
||||
<br/>
|
||||
|
||||
<h3><u>Technical Skills:</u></h3>
|
||||
|
||||
<ul>
|
||||
<li>Programming Languages: Elixir, C, C#, Java, Python, Fortran, Ada, Cobol</li>
|
||||
<li>Web Design and Coding: HTML, Javascript, ReactJs, CSS, Node.js, JQuery</li>
|
||||
<li>Software Environments: Windows, Mac, Linux, Unity, Godot, VirtualBox, Terminal/Console, Docker</li>
|
||||
<li>Various automation tools such as Bash, Testcafe, ExUnit, Jenkins, Gitlab CI and Python scripting</li>
|
||||
<li>Knowledge and experience with computer security, security vulnerabilities and security testing</li>
|
||||
<li>Well versed in application design, development and testing within an agile development cycle</li>
|
||||
<li>Expertise using SVN such as Github, Gitlab and Bitbucket within team and solo projects</li>
|
||||
</ul>
|
||||
|
||||
<br/><br/>
|
||||
|
||||
<blockquote></blockquote>
|
||||
|
||||
<br/>
|
||||
|
||||
<hr />
|
||||
</body>
|
||||
</div>
|
||||
)
|
||||
<h3>Technical Skills</h3>
|
||||
<ul>
|
||||
<li>Programming Languages: Elixir, C, C#, Java, Python, Fortran, Ada, Cobol</li>
|
||||
<li>Web Design and Coding: HTML, JavaScript, ReactJS, CSS, Node.js, jQuery</li>
|
||||
<li>Software Environments: Windows, Mac, Linux, Unity, Godot, VirtualBox, Docker</li>
|
||||
<li>Automation tools: Bash, TestCafe, ExUnit, Jenkins, GitLab CI, Python scripting</li>
|
||||
<li>Experience with security testing, vulnerabilities, and application design in agile environments</li>
|
||||
<li>Version control: GitHub, GitLab, Bitbucket</li>
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
+16
-30
@@ -4,36 +4,22 @@ import linkedin from "./../assets/LI-Logo.png";
|
||||
|
||||
const Footer = () => {
|
||||
return (
|
||||
<>
|
||||
<nav className="footer">
|
||||
<div className="footer-links">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/gjcampbell777">
|
||||
<img
|
||||
src={github}
|
||||
alt="Github Logo"
|
||||
height="auto"
|
||||
width="100%"
|
||||
style={{ maxWidth: "80px" }}
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.linkedin.com/in/gregoryjcampbell/">
|
||||
<img
|
||||
src={linkedin}
|
||||
alt="LinkedIn Logo"
|
||||
height="auto"
|
||||
width="100%"
|
||||
style={{ maxWidth: "115px" }}
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</>
|
||||
<footer className="footer">
|
||||
<div className="footer-links">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/gjcampbell777" target="_blank" rel="noreferrer noopener">
|
||||
<img src={github} alt="GitHub profile" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.linkedin.com/in/gregoryjcampbell/" target="_blank" rel="noreferrer noopener">
|
||||
<img src={linkedin} alt="LinkedIn profile" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -20,30 +20,22 @@ const Styles = styled.div`
|
||||
|
||||
export const NavigationBar = () => (
|
||||
<Styles>
|
||||
<Navbar expand="lg" color='light-blue lighten-4'>
|
||||
<Navbar expand="lg" bg="light" variant="light">
|
||||
<Navbar.Brand as={Link} to="/">GC</Navbar.Brand>
|
||||
<Navbar.Toggle aria-controls="basic-navbar-nav" />
|
||||
<Navbar.Collapse id="basic-navbar-nav">
|
||||
<Nav classname="navbar-link">
|
||||
<Nav className="navbar-link">
|
||||
<Nav.Item>
|
||||
<Nav.Link as={Link} to="/">
|
||||
<Link to="/">Home</Link>
|
||||
</Nav.Link>
|
||||
<Nav.Link as={Link} to="/">Home</Nav.Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Nav.Link as={Link} to="/Resume">
|
||||
<Link to="/Resume">Resume</Link>
|
||||
</Nav.Link>
|
||||
<Nav.Link as={Link} to="/Resume">Resume</Nav.Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Nav.Link as={Link} to="/Projects">
|
||||
<Link to="/Projects">Projects</Link>
|
||||
</Nav.Link>
|
||||
<Nav.Link as={Link} to="/Projects">Projects</Nav.Link>
|
||||
</Nav.Item>
|
||||
<Nav.Item>
|
||||
<Nav.Link as={Link} to="/Contact">
|
||||
<Link to="/Contact">Contact</Link>
|
||||
</Nav.Link>
|
||||
<Nav.Link as={Link} to="/Contact">Contact</Nav.Link>
|
||||
</Nav.Item>
|
||||
</Nav>
|
||||
</Navbar.Collapse>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
|
||||
export const ProjectCard = ({ title, description, imgSrc, imgAlt, link, children }) => {
|
||||
|
||||
return (
|
||||
<article className="project-card">
|
||||
<h3>{link ? <a href={link} target="_blank" rel="noreferrer noopener">{title}</a> : title}</h3>
|
||||
<p>{description}</p>
|
||||
{imgSrc && (
|
||||
<img
|
||||
src={imgSrc}
|
||||
alt={imgAlt || title}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
)}
|
||||
{children}
|
||||
</article>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProjectCard;
|
||||
+20
-15
@@ -12,32 +12,30 @@ code {
|
||||
monospace;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
width: 15%;
|
||||
float: right;
|
||||
}
|
||||
|
||||
a{
|
||||
a {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #219837
|
||||
color: #219837;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.homeimage{
|
||||
.homeimage {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: auto;
|
||||
max-width: 480px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.iframe-container {
|
||||
@@ -58,10 +56,9 @@ img {
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
color: white;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
padding: 1rem 0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.footer-links ul {
|
||||
@@ -83,4 +80,12 @@ img {
|
||||
|
||||
.footer-links li:hover {
|
||||
background-color: #b2b2b2;
|
||||
}
|
||||
|
||||
/* Constrain footer images so they don't become massive */
|
||||
.footer-links img {
|
||||
display: inline-block;
|
||||
max-height: 120px;
|
||||
width: auto;
|
||||
vertical-align: middle;
|
||||
}
|
||||
Reference in New Issue
Block a user