This commit is contained in:
Gregory Campbell
2026-06-04 17:34:17 -04:00
24 changed files with 32400 additions and 12407 deletions
+42
View File
@@ -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
+5
View File
@@ -21,3 +21,8 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# amplify
.amplify
amplify_outputs*
amplifyconfiguration*
+34 -1
View File
@@ -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.
[![Netlify Status](https://api.netlify.com/api/v1/badges/b9a5df97-64f4-4888-8d76-548e77e029ae/deploy-status)](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
View File
@@ -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/**/
+11
View File
@@ -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,
},
});
+11
View File
@@ -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,
});
+53
View File
@@ -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>
+3
View File
@@ -0,0 +1,3 @@
{
"type": "module"
}
+17
View File
@@ -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/*"
]
}
}
}
+31752 -1475
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -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
View File
@@ -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>
+47
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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:&nbsp;
<a href="mailto:gjcampbell777@gmail.com">gjcampbell777@gmail.com </a>
</h2>
<h2>Cell #:&nbsp;
<a href="tel:6477797951">(647)-779-7951</a>
</h2>
<h2>Twitter:&nbsp;
<a href="https://twitter.com/Gregory_Soup">@Gregory_Soup</a>
</h2>
<h2>LinkedIn:&nbsp;
<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
View File
@@ -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
View File
@@ -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&nbsp;
<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
View File
@@ -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 &ndash; 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 &ndash; 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) &ndash; 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
View File
@@ -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>
);
};
+6 -14
View File
@@ -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>
+22
View File
@@ -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
View File
@@ -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;
}
-10400
View File
File diff suppressed because it is too large Load Diff