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* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.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) [![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). 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", "repository": "https://github.com/gjcampbell777/personal_site.git",
"author": "Gregory Campbell <gjcampbell777@gmail.com>", "author": "Gregory Campbell <gjcampbell777@gmail.com>",
"license": "MIT", "license": "MIT",
"homepage": ".",
"dependencies": { "dependencies": {
"@testing-library/jest-dom": "^5.11.1", "@testing-library/jest-dom": "^5.11.1",
"@testing-library/react": "^11.2.5", "@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^12.0.11", "@testing-library/user-event": "^12.0.11",
"ajv": "^8.12.0",
"aws-amplify": "^6.17.0",
"bootstrap": "^5.0.0", "bootstrap": "^5.0.0",
"immer": "9.0.6", "immer": "9.0.6",
"ini": "2.0.0", "ini": "2.0.0",
@@ -18,6 +21,8 @@
"react": "^17.0.1", "react": "^17.0.1",
"react-bootstrap": "^1.2.2", "react-bootstrap": "^1.2.2",
"react-dom": "^17.0.1", "react-dom": "^17.0.1",
"react-helmet-async": "^1.3.0",
"react-is": "^19.2.7",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"styled-components": "^5.1.1" "styled-components": "^5.1.1"
@@ -42,5 +47,15 @@
"last 1 firefox version", "last 1 firefox version",
"last 1 safari 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"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <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="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta <meta
name="description" name="description"
content="The Personal Website of Gregory Campbell" 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 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/ 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 <link
rel="stylesheet" rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
crossorigin="anonymous" 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> <title>Gregory J Campbell</title>
</head> </head>
<body> <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 = () => ( export const NoMatch = () => {
<div> return (
<head> <main className="notfound-page">
<title>404 ERROR</title> <Helmet>
</head> <title>404 Error</title>
<body> </Helmet>
<header>
<h1>404 ERROR</h1>
<aside>Hey, you shouldn't be here!</aside> <header>
</header> <h1>404 ERROR</h1>
<p>Hey, you shouldn't be here!</p>
<br/> </header>
<section>
<h2>Don't know how you got here but you should probably turn around and head back.</h2> <p>Don't know how you got here, but you should probably head back.</p>
</section>
<p>There's nothing here!</p> </main>
</body> );
</div> };
)
+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 { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { HelmetProvider } from 'react-helmet-async';
import { Home } from './Home'; import { Home } from './Home';
import { Resume } from './Resume'; import { Resume } from './Resume';
import { Projects } from './Projects'; import { Projects } from './Projects';
@@ -10,27 +11,23 @@ import { NavigationBar } from './components/NavigationBar';
import { Jumbotron } from './components/Jumbotron'; import { Jumbotron } from './components/Jumbotron';
import Footer from './components/Footer'; import Footer from './components/Footer';
class App extends Component { const App = () => (
render() { <HelmetProvider>
return ( <Router>
<React.Fragment> <NavigationBar />
<Router> <Jumbotron />
<NavigationBar /> <Layout>
<Jumbotron /> <Switch>
<Layout> <Route exact path="/" component={Home} />
<Switch> <Route path="/Resume" component={Resume} />
<Route exact path="/" component={Home} /> <Route path="/Projects" component={Projects} />
<Route path="/Resume" component={Resume} /> <Route path="/Contact" component={Contact} />
<Route path="/Projects" component={Projects} /> <Route component={NoMatch} />
<Route path="/Contact" component={Contact} /> </Switch>
<Route component={NoMatch} /> </Layout>
</Switch> <Footer />
</Layout> </Router>
<Footer /> </HelmetProvider>
</Router> );
</React.Fragment>
);
}
}
export default App; 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 = () => ( export const Contact = () => {
<div> return (
<head> <main className="contact-page">
<title>Contact Info</title> <Helmet>
</head> <title>Contact Gregory Campbell</title>
<body> <meta name="description" content="Contact details for Gregory Campbell." />
<header> </Helmet>
<h1>How to Reach Me</h1>
<aside>Give me a shout!</aside> <header>
</header> <h1>How to Reach Me</h1>
<p>Give me a shout!</p>
</header>
<br/> <section>
<h2>Contact Info</h2>
<h2>Contact Info:</h2> <ul className="contact-list">
<li>Email: <a href="mailto:gjcampbell777@gmail.com">gjcampbell777@gmail.com</a></li>
<br/> <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>
<h2>Email:&nbsp; </ul>
<a href="mailto:gjcampbell777@gmail.com">gjcampbell777@gmail.com </a> </section>
</h2> </main>
);
<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>
)
+33 -46
View File
@@ -1,50 +1,37 @@
import React from 'react' import React from 'react';
import Me from './assets/Me.jpeg' import { Helmet } from 'react-helmet-async';
import meImage from './assets/Me.jpeg';
export const Home = () => ( export const Home = () => {
<div> return (
<head> <main className="home-page">
<title>The Works of Gregory Campbell</title> <Helmet>
</head> <title>The Works of Gregory Campbell</title>
<body> <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> <section>
<img
<p></p> src={meImage}
alt="Portrait of Gregory Campbell"
<ul> loading="lazy"
<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> decoding="async"
</ul> />
</section>
<p></p> </main>
);
<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>
)
+153 -236
View File
@@ -1,236 +1,153 @@
import React from 'react' import React from 'react';
import ShowingOff from './assets/ShowingOff.jpg' import { Helmet } from 'react-helmet-async';
import MJB from './assets/MJB.jpg' import ProjectCard from './components/ProjectCard';
import GC from './assets/GunControl.png'
import Palish from './assets/Palish.png' // Import all project images
import SRS from './assets/SRS.png' import StateMachine from './assets/StateMachine.png';
import WH from './assets/W&H.png' import HSV from './assets/HSV.png';
import StateMachine from './assets/StateMachine.png' import skin2 from './assets/skin2.png';
import Skin2 from './assets/skin2.png' import EulerExample from './assets/EulerExample.png';
import HSV from './assets/HSV.png' import EulerCycle from './assets/EulerCycle.png';
import EulerCycle from './assets/EulerCycle.png' import Hangman from './assets/Hangman.png';
import EulerExample from './assets/EulerExample.png' import KnightsTour from './assets/KnightsTour.png';
import Hangman from './assets/Hangman.png' import TextAnalyzer from './assets/TextAnalyzer.png';
import KngihtsTour from './assets/KnightsTour.png' import AlgorithmComparison from './assets/AlgorithmComparison.png';
import TextAnalyzer from './assets/TextAnalyzer.png' import ShowingOff from './assets/ShowingOff.jpg';
import AlgorithmComparison from './assets/AlgorithmComparison.png' import MJB from './assets/MJB.jpg';
import GunControl from './assets/GunControl.png';
export const Projects = () => ( import Palish from './assets/Palish.png';
<div> import WH from './assets/W&H.png';
<head> import SRS from './assets/SRS.png';
<title>The Projects of Gregory Campbell</title>
</head>
<body> // Map imgName to imported images
<header> const imageMap = {
<h1>My Projects</h1> StateMachine,
HSV,
<aside>Look at what I've created!</aside> skin2,
</header> EulerExample,
EulerCycle,
<br/> Hangman,
KnightsTour,
<h2>This page contains the projects I've done over the past, present and future.</h2> TextAnalyzer,
AlgorithmComparison,
<br/> ShowingOff,
MJB,
<hr/> GunControl,
Palish,
<h4>If you want to see what I'm currently working on you can see all my updates&nbsp; WH,
<a href="https://twitter.com/Gregory_Soup">Here</a> SRS
</h4> };
<hr/> const projects = [
<h3> <a href="https://github.com/gjcampbell777/python-state-machine">Python State Machine</a> </h3> {
title: 'Python State Machine',
<br/> link: 'https://github.com/gjcampbell777/python-state-machine',
description: 'A Python state machine used to emulate a locked/unlocked device.',
<p>Recently while going through Godot documentation I came across a tutorial for building your own state machine with Python. imgName: 'StateMachine',
I've always been interested in making a stand alone state machine and state machines are a big part of game developement imgAlt: 'Python state machine demo'
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>. title: 'Python Skin Detection Algorithms',
</p> link: 'https://github.com/gjcampbell777/python-photo-skin-detection',
description: 'Replicated multiple skin detection algorithms in Python and modernized them for Python 3.',
<img src={StateMachine} alt="StateMachine"/> imgName: 'HSV',
imgAlt: 'Skin detection image after',
<br/><br/> },
{
<h3> <a href="https://github.com/gjcampbell777/python-photo-skin-detection">Python Skin Detection Algorithms</a> </h3> imgName: 'skin2',
imgAlt: 'Skin detection image before',
<br/> },
{
<p>In an image processing course I took in university we were tasked with replicating multiple skin detection algorithms through a python script. title: 'Euler Checking Algorithm',
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. link: 'https://github.com/gjcampbell777/euler-checking-algorithm',
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 description: 'C program to check if graphs are Eulerian.',
and clean it up as well as make sure it can run with python3. imgName: 'EulerExample',
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>. imgAlt: 'Euler example'
</p> },
{
<img src={Skin2} alt="Before"/> imgName: 'EulerCycle',
imgAlt: 'Euler cycle output',
<p>Before going through the algorithm</p> },
{
<br/> title: 'Fortran Hangman',
link: 'https://github.com/gjcampbell777/fortran-hangman',
<img src={HSV} alt="After"/> description: 'Updated a Fortran 77 hangman game to Fortran 95 and improved the code.',
imgName: 'Hangman',
<p>After going through the HSV algorithm</p> imgAlt: 'Hangman game'
},
<br/> {
title: "Ada Knights Tour",
<h3> <a href="https://github.com/gjcampbell777/euler-checking-algorithm">Euler Checking Algorithm</a> </h3> link: 'https://github.com/gjcampbell777/ada-knights-tour',
description: "Solves the knight's tour problem in Ada.",
<br/> imgName: 'KnightsTour',
imgAlt: 'Knights tour output'
<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. title: 'COBOL Text Analyzer',
This C algorithm takes the text file euler-graphs.txt and runs through the input to determine if its contents contain a Euler graph. link: 'https://github.com/gjcampbell777/cobol-text-analyzer',
The algorithm checks the initial line of a graph to determine the length of the graph and description: 'Modernized COBOL text analyzer to report text statistics.',
uses that information to know how many lines it must go through to gather all the info of the graph. imgName: 'TextAnalyzer',
It will repeat this process until it reaches EOF. imgAlt: 'Text analyzer output'
You can check out my code by checking out the repo <a href="https://github.com/gjcampbell777/euler-checking-algorithm">Here</a>. },
</p> {
title: 'Legacy Algorithm Comparison',
<br/> link: 'https://github.com/gjcampbell777/legacy-algorithm-comparison',
description: 'Compare KMP implementations across C, Fortran, and Ada.',
<img src={EulerExample} alt="EulerExample"/> imgName: 'AlgorithmComparison',
imgAlt: 'Algorithm comparison charts'
<p>Example of a graph from euler-graphs.txt</p> },
{
<br/> title: 'My Many Games',
description: 'A selection of games made with Unity and Godot.',
<img src={EulerCycle} alt="EulerCycle"/> imgName: 'ShowingOff',
imgAlt: 'Game showcase'
<p>Output from running the algorithm with euler-graphs.txt</p> },
{
<br/> imgName: 'MJB',
imgAlt: 'Magical Jumping Bean'
<h3> <a href="https://github.com/gjcampbell777/fortran-hangman">Fortran Hangman</a> </h3> },
{
<br/> imgName: 'GunControl',
imgAlt: 'Gun Control'
<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 imgName: 'Palish',
from those days and wanted to re-explore and improve upon. imgAlt: 'Palish'
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>. imgName: 'WH',
</p> imgAlt: 'Wally And Harv Escape the Fireball and Brick Factory'
},
<br/> {
imgName: 'SRS',
<img src={Hangman} alt="Hangman"/> imgAlt: 'Super Robo Squad'
<p>A very quick game of hangman</p> },
];
<br/>
export const Projects = () => {
<h3> <a href="https://github.com/gjcampbell777/ada-knights-tour">Ada Kngihts Tour</a> </h3>
return (
<br/> <main className="projects-page">
<Helmet>
<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. <title>The Projects of Gregory Campbell</title>
Much like some of the other projects above this was also an assignment I could still remember <meta name="description" content="Collection of projects by Gregory Campbell." />
from those days and wanted to re-explore and improve upon. </Helmet>
Run the executable and follow the prompts to have the program solve the problem for you. <header>
However you should try to not make the chessboard size bigger than 7 if you don't have time to spare. <h1>My Projects</h1>
You can check out my code for this project <a href="https://github.com/gjcampbell777/ada-knights-tour">Here</a>. <aside>Look at what I've created!</aside>
</p> </header>
<br/> {projects.map((p) => (
<ProjectCard
<img src={KngihtsTour} alt="KnightsTour"/> key={p.title}
title={p.title}
<p>Knights tour output for a 5x5 chessboard starting at position (1,1)</p> description={p.description}
imgSrc={imageMap[p.imgName]}
<br/> imgAlt={p.imgAlt}
link={p.link}
<h3> <a href="https://github.com/gjcampbell777/cobol-text-analyzer">Cobol Text Analyzer</a> </h3> />
))}
<br/> </main>
);
<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>
)
+78 -93
View File
@@ -1,103 +1,88 @@
import React from 'react' import React from 'react';
import pdf from './assets/Gregory Campbell Resume.pdf' import { Helmet } from 'react-helmet-async';
import pic from './assets/Resume.png' import pdf from './assets/Gregory Campbell Resume.pdf';
import resumeImage from './assets/Resume.png';
export const Resume = () => ( export const Resume = () => {
<div> return (
<head> <main className="resume-page">
<title>The Resume of Gregory Campbell</title> <Helmet>
</head> <title>The Resume of Gregory Campbell</title>
<body> <meta name="description" content="Resume and professional experience of Gregory Campbell." />
<header> </Helmet>
<h1>My Resume</h1>
<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/> <h3>Technical Skills</h3>
<ul>
<h2><u>Written Resume</u></h2> <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>
<br/> <li>Software Environments: Windows, Mac, Linux, Unity, Godot, VirtualBox, Docker</li>
<li>Automation tools: Bash, TestCafe, ExUnit, Jenkins, GitLab CI, Python scripting</li>
<h2>Gregory Campbell</h2> <li>Experience with security testing, vulnerabilities, and application design in agile environments</li>
<li>Version control: GitHub, GitLab, Bitbucket</li>
<p align="right"> </ul>
<a href="tel:6477797951">(647)-779-7951</a><br /> </section>
<b><a href="mailto:gjcampbell777@gmail.com">gjcampbell777@gmail.com </a></b> </main>
</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>
)
+16 -30
View File
@@ -4,36 +4,22 @@ import linkedin from "./../assets/LI-Logo.png";
const Footer = () => { const Footer = () => {
return ( return (
<> <footer className="footer">
<nav className="footer"> <div className="footer-links">
<div className="footer-links"> <ul>
<ul> <li>
<li> <a href="https://github.com/gjcampbell777" target="_blank" rel="noreferrer noopener">
<a href="https://github.com/gjcampbell777"> <img src={github} alt="GitHub profile" />
<img </a>
src={github} </li>
alt="Github Logo" <li>
height="auto" <a href="https://www.linkedin.com/in/gregoryjcampbell/" target="_blank" rel="noreferrer noopener">
width="100%" <img src={linkedin} alt="LinkedIn profile" />
style={{ maxWidth: "80px" }} </a>
/> </li>
</a> </ul>
</li> </div>
<li> </footer>
<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>
</>
); );
}; };
+6 -14
View File
@@ -20,30 +20,22 @@ const Styles = styled.div`
export const NavigationBar = () => ( export const NavigationBar = () => (
<Styles> <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.Brand as={Link} to="/">GC</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" /> <Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav"> <Navbar.Collapse id="basic-navbar-nav">
<Nav classname="navbar-link"> <Nav className="navbar-link">
<Nav.Item> <Nav.Item>
<Nav.Link as={Link} to="/"> <Nav.Link as={Link} to="/">Home</Nav.Link>
<Link to="/">Home</Link>
</Nav.Link>
</Nav.Item> </Nav.Item>
<Nav.Item> <Nav.Item>
<Nav.Link as={Link} to="/Resume"> <Nav.Link as={Link} to="/Resume">Resume</Nav.Link>
<Link to="/Resume">Resume</Link>
</Nav.Link>
</Nav.Item> </Nav.Item>
<Nav.Item> <Nav.Item>
<Nav.Link as={Link} to="/Projects"> <Nav.Link as={Link} to="/Projects">Projects</Nav.Link>
<Link to="/Projects">Projects</Link>
</Nav.Link>
</Nav.Item> </Nav.Item>
<Nav.Item> <Nav.Item>
<Nav.Link as={Link} to="/Contact"> <Nav.Link as={Link} to="/Contact">Contact</Nav.Link>
<Link to="/Contact">Contact</Link>
</Nav.Link>
</Nav.Item> </Nav.Item>
</Nav> </Nav>
</Navbar.Collapse> </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; monospace;
} }
span { a {
display: inline-block;
width: 15%;
float: right;
}
a{
color: #000; color: #000;
text-decoration: none;
} }
a:hover { a:hover {
color: #219837 color: #219837;
} }
img { img {
display: block; display: block;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
width: 100%; max-width: 100%;
height: auto;
} }
.homeimage{ .homeimage {
display: block; display: block;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
width: auto; max-width: 480px;
width: 100%;
height: auto;
} }
.iframe-container { .iframe-container {
@@ -58,10 +56,9 @@ img {
.footer { .footer {
display: flex; display: flex;
justify-content: center; justify-content: center;
position: sticky; position: relative;
top: 0; padding: 1rem 0;
color: white; color: #000;
z-index: 1;
} }
.footer-links ul { .footer-links ul {
@@ -83,4 +80,12 @@ img {
.footer-links li:hover { .footer-links li:hover {
background-color: #b2b2b2; 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