diff --git a/src/App.scss b/src/App.scss index 853c4cb..ac93886 100644 --- a/src/App.scss +++ b/src/App.scss @@ -210,10 +210,42 @@ $purple200: #ce93d8; } .projects-page { - .MuiPaper-root { - padding: 1rem; - width: 75%; - height: 15rem; - margin-top: 3rem; + .filter-panel { + display: flex; + flex-direction: column; + background-color: white; + align-items: center; + padding: 2rem; + width: 50%; + .filter-controls { + display: inline-flex; + justify-content: space-between; + width: 65%; + } + } + + .project-cards { + display: flex; + flex-direction: column; + align-items: center; + + > * { + display: flex; + flex-direction: column; + align-items: center; + background-color: lightblue; + width: 80%; + height: auto; + margin-bottom: 2rem; + .links { + display: flex; + width: 75%; + align-items: baseline; + justify-content: space-around; + } + a:last-child { + margin-bottom: 1rem; + } + } } } \ No newline at end of file diff --git a/src/pages/Projects.js b/src/pages/Projects.js index 899f24c..d793ac0 100644 --- a/src/pages/Projects.js +++ b/src/pages/Projects.js @@ -1,5 +1,5 @@ import '../App.scss'; -import { useState } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { DocumentStyle, ProjectsPage } from '../styles/Style'; @@ -9,94 +9,254 @@ import Card from '@mui/material/Card'; const { htmlTheme, stockGallery } = DocumentStyle; const { projectsButton } = ProjectsPage; +const defaultFilter = { + language: '', + searchTerm: '', + inProgress: null +} + export default function Projects() { - const galleryArray = [ - ( - -

Reddit, but it's all cats

-

A read-only Reddit client -- this site fetches data from Reddit and displays a curated feed.

-

This was built on Reddit's JSON API, using React/Redux and CSS.

-

And yes, it's all cats.

- View the deployed project! -
- ), - ( - -

Personal Timestamp Generator

-

A small-scale timestamp/productivity management tool for individual use and logging of - time, including compartmentalization by task and some aggregate functions based on queries. -

-

Command-line interface built on Python with a SQLite Database.

- View the repo here! -
- ), - ( - -

Musical Counterpoint Bot

-

A web-based program with functionality to evaluate sample solutions of problems in - species-based counterpoint, as detailed by Johann Fux in Gradus ad Parnassum.

-

This project is intended to be used as a practical application of linked lists and - other compound data structures in JavaScript.

-

In progress. Using vanilla HTML/CSS/JS.

-
- ), - ( - -

(untitled) Online Guess-the-Word Game

-

As part of a mentorship program hosted by Metazu Studio

-

Implemented using MongoDB, React, and Node/Express, styled with Material UI/SCSS.

-

In progress, building in collaboration with others at Metazu Studio.

-
- ), - ( - -

Splinter

-

A clone of a popular card-based resource gathering game

-

Local multiplayer, with plans to build out online multiplayer and solo vs. CPU

-

In progress. Using React, Node/Express, and PostgreSQL.

-
- ), - ( - -

Carenest

-

Designed in collaboration with Faith Magras, Elvis Hernandez, and Daytreon Dean - as a submission for #HACKTN in March 2022.

-

Produced using React. View the repo here!

-
- ), - ( - -

And, last but not least, the site you see here!

-

This site is built using React, Material UI, and SCSS, and is hosted on Github Pages.

-
- ), - ] + const [results, setResults] = useState(); + const [filter, setFilter] = useState(); - const [galleryIndex, setGalleryIndex] = useState(0); + const searchInput = useRef(); + const languageFilter = useRef(); - const handleDecrement = () => { - setGalleryIndex((prev) => { - let newValue = prev - 1; - if (newValue === -1) { - newValue = galleryArray.length - 1; + useEffect(() => { + const galleryArray = [ + { + name: "Mikayla's E-Commerce Store", + languages: ["TypeScript", "React", "PERN", "REST_API"], + inProgress: true, + jsx: ( + +

Mikayla's E-Commerce Store

+

A fully-featured e-commerce platform

+

Built in React with TypeScript, Node/Express, and PostgreSQL

+

Payment processing supported through Stripe

+

REST API fully documented in Swagger

+

IN PROGRESS

+
+ ) + }, + { + name: "Procedural Drones", + languages: ["JavaScript", "HTML/CSS"], + inProgress: true, + deployed: true, + jsx: ( + +

Procedural Drones

+

An experimental space for building out procedural music generation using vanilla JavaScript

+

Uses Tone.js to interact with the Web Audio API

+

Features a plain HTML/CSS front end to interact with the program

+
+ GitHub Repo + Deployed version +
+
+ ) + }, + { + name: "Reddit, but it's all cats", + languages: ["React", "Redux"], + inProgress: false, + deployed: true, + jsx: ( + +

Reddit, but it's all cats

+

A read-only Reddit client -- this site fetches data from Reddit and displays a curated feed.

+

This was built on Reddit's JSON API, using React/Redux and CSS.

+

And yes, it's all cats.

+
+ GitHub Repo + Deployed version +
+
+ ) + }, + { + name: "Personal Timestamp Generator", + languages: ["Python", "SQLite"], + inProgress: false, + jsx: ( + +

Personal Timestamp Generator

+

A small-scale timestamp/productivity management tool for individual use and logging of + time, including compartmentalization by task and some aggregate functions based on queries. +

+

Command-line interface built on Python with a SQLite Database.

+ View the repo here! +
+ ) + }, + { + name: "Musical Counterpoint Bot", + languages: ["HTML/CSS", "JavaScript"], + inProgress: true, + jsx: ( + +

Musical Counterpoint Bot

+

A web-based program with functionality to evaluate sample solutions of problems in + species-based counterpoint, as detailed by Johann Fux in Gradus ad Parnassum.

+

This project is intended to be used as a practical application of linked lists and + other compound data structures in JavaScript.

+

In progress. Using vanilla HTML/CSS/JS.

+
+ ) + }, + { + name: "Password Game", + languages: ["React", "MongoDB", "MERN", "React", "REST_API", "Socket.io", "Sass", "MaterialUI"], + inProgress: true, + jsx: ( + +

Password Game

+

As part of a mentorship program hosted by Metazu Studio

+

Implemented using MongoDB, React, and Node/Express, styled with Material UI/SCSS.

+

In progress, building in collaboration with others at Metazu Studio.

+
+ ) + }, + { + name: "Splinter", + languages: ["React", "PERN", "Socket.io"], + inProgress: true, + jsx: ( + +

Splinter

+

A clone of a popular card-based resource gathering game

+

Local multiplayer, with plans to build out online multiplayer and solo vs. CPU

+

In progress. Using React, Node/Express, and PostgreSQL.

+
+ ) + }, + { + name: "Carenest", + languages: ["React"], + inProgress: false, + jsx: ( + +

Carenest

+

Designed in collaboration with Faith Magras, Elvis Hernandez, and Daytreon Dean + as a submission for #HACKTN in March 2022.

+

Produced using React. View the repo here!

+
+ ) + }, + { + name: "This Site", + languages: ["React", "Sass", "MaterialUI"], + inProgress: true, + jsx: ( + +

And, last but not least, the site you see here!

+

This site is built using React, Material UI, and SCSS, and is hosted with Netlify.

+ View the site repo here! +
+ ) } - return newValue; - }); + ] + + if (!filter) { + setResults(galleryArray.map(each => each.jsx)); + } else { + let filteredRes = galleryArray; + + if (filter.language) { + for (let element of filteredRes) { + if (filter.language === "Express" && element.languages.includes("PERN" || "MERN")) { + continue; + } else if (filter.language === "PostgreSQL" && element.languages.includes("PERN")) { + continue; + } else if (element.languages.includes(filter.language)) { + continue; + } else { + filteredRes.splice(filteredRes.indexOf(element), 0, ''); + continue; + } + } + } + + if (filter.searchTerm) { + let caseSafeTerm = filter.searchTerm.toLowerCase(); + + for (let element of filteredRes) { + for (let each of element.languages) { + if (each.toLowerCase().includes(caseSafeTerm)) { + continue; + } + } + + if (element.name.toLowerCase().includes(caseSafeTerm)) { + continue; + } else { + filteredRes.splice(filteredRes.indexOf(element), 0, ''); + continue; + } + } + } + + for (let element of filteredRes) { + if (element.inProgress === filter.inProgress) { + continue; + } else { + filteredRes.splice(filteredRes.indexOf(element), 0, ''); + continue; + } + } + + setResults(filteredRes.map(each => each.jsx)); + } + }, [filter]); + + const handleChange = (e) => { + e.preventDefault(); + setFilter({ + ...filter, + language: e.target.value + }) } - const handleIncrement = () => { - setGalleryIndex(prev => (prev + 1) % galleryArray.length); + const handleReset = () => { + setFilter(defaultFilter); + searchInput.current.value = ''; + languageFilter.current.value = ''; } return (

Here are some sample projects from my portfolio!

-
- - {galleryArray[galleryIndex]} - -
+
+

Filter by:

+
+ setFilter({...filter, searchTerm: e.target.value})} + placeholder="Enter a search term"> + + + + +
+
+ +
{results}
) } \ No newline at end of file