diff --git a/components/bin/GalleryPosts.js b/components/bin/GalleryPosts.js index e5cf05a5..bd611c2e 100644 --- a/components/bin/GalleryPosts.js +++ b/components/bin/GalleryPosts.js @@ -1,10 +1,10 @@ import React from 'react' import styles from '../../public/bin/style/gallery.module.css' +import PartTag from './PartTag'; import { useEffect, useRef, useState } from 'react'; -const BinPost = ({title = "Bin Post", desc = "Bin Project", slack = '', link = '', id, date}) => { - +const BinPost = ({title = "Bin Post", desc = "Bin Project", slack = '', link = '', id, date, parts}) => { link = link.trim(); if (!/^https?:\/\//i.test(link)) { link = 'https://' + link; @@ -37,6 +37,10 @@ const BinPost = ({title = "Bin Post", desc = "Bin Project", slack = '', link = ' return inputDate.toLocaleDateString(undefined, options); } } + if (parts){ + parts = parts.filter(part => part !== "recvK14pXAY1tn3HQ" && part !== "rec5TQNvkGkscsGuQ"); //Filter out breadboards and raspberry pi + } + return (

{desc}

- {(slack ? (slack.startsWith('@') ? (slack) : (`@${slack}`)) : (""))+ " "} + {(slack ? (slack.startsWith('@') ? (slack) : (`@${slack}`)) : (""))+ " "} {formatDate(date)} - +
+ {parts && parts.map(part => { + return ( + ) + + })} +
) } diff --git a/components/bin/PartTag.js b/components/bin/PartTag.js new file mode 100644 index 00000000..a89cd691 --- /dev/null +++ b/components/bin/PartTag.js @@ -0,0 +1,151 @@ +import React from 'react' +import styles from './PartTag.module.css' +import { useState } from 'react'; + +const PartTag = ({ partID, search = false, addFilter, removeFilter}) => { + const [isOutlined, setIsOutlined] = useState(false); + + const handleClick = () => { + if (search){ + setIsOutlined(prevState => !prevState); + if (isOutlined){ + removeFilter(partID); + } else { + addFilter(partID); + } + } + + }; + + let backgroundColor = ''; + let text = ''; + switch (partID) { + case "recltWikgPdLvpJfe": + backgroundColor = '#0000FF'; // Vibrant blue + text = 'Servo'; + break; + case "recRzllr0dui91NLd": + backgroundColor = '#008000'; // Vibrant green + text = 'LED'; + break; + case "recM7OOofV9Bp7AM9": + backgroundColor = '#FF0000'; // Vibrant red + text = 'ESP32'; + break; + case "recALoD1CCKt3CxKE": + backgroundColor = '#800080'; // Vibrant purple + text = 'Buzzer'; + break; + case "rechtwyljZ5WR8DtR": + backgroundColor = '#FF4500'; // Vibrant orange + text = 'Slider'; + break; + case "recry1GsMO6QLakzw": + backgroundColor = '#8B4513'; // Dark brown + text = 'Photoresistor'; + break; + case "recjRu1vTAU3qDanE": + backgroundColor = '#FF1493'; // Vibrant pink + text = 'LCD'; + break; + case "recrgS7NnxS42tkmg": + backgroundColor = '#A52A2A'; // Vibrant brown + text = 'LED Screen'; + break; + case "recocuypi4xP0UgAj": + backgroundColor = '#000000'; // Black + text = 'Joystick'; + break; + case "recgLUxtFZHufN70W": + backgroundColor = '#1E90FF'; // Dodger blue + text = 'LED Bar Graph'; + break; + case "recKBAnftT9PgppUC": + backgroundColor = '#00FFFF'; // Vibrant cyan + text = 'Shift Register'; + break; + case "recibIXNCSdhDHjXD": + backgroundColor = '#FF00FF'; // Vibrant magenta + text = 'Thermistor'; + break; + case "recwSKHd3anpKqNbg": + backgroundColor = '#00FF00'; // Vibrant lime + text = 'IR Receiver'; + break; + case "recLRovQNumB1Et8B": + backgroundColor = '#008080'; // Vibrant teal + text = 'Range Finder'; + break; + case "recMVBkeJ4KQdZihl": + backgroundColor = '#808000'; // Vibrant olive + text = 'Keypad'; + break; + case "recGrj5GpSExI18Ff": + backgroundColor = '#000080'; // Vibrant navy + text = 'Humidity'; + break; + case "rec9G0CAXM0kdp7HY": + backgroundColor = '#800000'; // Vibrant maroon + text = 'RTC'; + break; + case "rec4vTiJIx4UP8Thl": + backgroundColor = '#DAA520'; // Goldenrod + text = 'Motion Sensor'; + break; + case "reczWN9rZOY95VXOT": + backgroundColor = '#FF8C00'; // Dark orange + text = 'LED Matrix'; + break; + case "recNjAmh8gF0gZNtI": + backgroundColor = '#FF6347'; // Tomato red + text = 'Accelerometer'; + break; + case "recPmyV5b8cvaMtTk": + backgroundColor = '#4B0082'; // Vibrant indigo + text = 'Neopixel LED'; + break; + case "recj5b4DKez4GNa8i": + backgroundColor = '#87CEEB'; // Vibrant sky blue + text = 'Relay'; + break; + case "rec5TQNvkGkscsGuQ": + backgroundColor = '#9932CC'; // Vibrant orchid + text = 'Pico W'; + break; + case "recqffGd1j1jRh56m": + backgroundColor = '#DDA0DD'; // Vibrant plum + text = 'Multicolor LED'; + break; + case "recJUolkJURydamzG": + backgroundColor = '#CD5C5C'; // Vibrant light coral + text = 'Encoder'; + break; + case "rec7lggt0DsgrWHzc": + backgroundColor = '#20B2AA'; // Vibrant light sea green + text = 'Temp Sensor'; + break; + case "rectVgu4kWbbaqccc": + backgroundColor = '#FFA07A'; // Vibrant light salmon + text = 'Button'; + break; + case "recWKEXSaByRvl68t": + backgroundColor = '#4682B4'; // Vibrant light steel blue + text = '4 Digit Display'; + break; + default: + backgroundColor = 'gray'; // Default gray + text = 'Invalid Tag'; + console.log("invalid", partID) + } + + + return ( +
+ {text} +
+ ) +} + +export default PartTag \ No newline at end of file diff --git a/components/bin/PartTag.module.css b/components/bin/PartTag.module.css new file mode 100644 index 00000000..af88a305 --- /dev/null +++ b/components/bin/PartTag.module.css @@ -0,0 +1,24 @@ +.tag{ + color: e1e1e1; + padding: 4px 10px; + border-radius: 20px; + width: fit-content; + max-width: 300px; + display: flex; + text-align: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + transition: transform 0.3s ease; + box-sizing: border-box; +} + +.tag:hover{ + cursor: pointer; + transform: scale(1.1); +} + +.outlined { + border: 5px dotted #c5c5c5; + } + diff --git a/components/bin/galleryView.js b/components/bin/galleryView.js deleted file mode 100644 index b2836464..00000000 --- a/components/bin/galleryView.js +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react' -import styles from '../../public/bin/style/gallery.module.css' -import { useEffect, useRef, useState } from 'react'; - - -const BinPost = ({title = "Bin Post", desc = "Bin Project", slack = '', link = '', id, date}) => { - - link = link.trim(); - if (!/^https?:\/\//i.test(link)) { - link = 'https://' + link; - } - const projectID = link.split('/')[4] - const imgLink = `https://thumbs.wokwi.com/projects/${projectID}/social/bin.png` - - - function handleClick() { - console.log("clicked"); - if (typeof window !== 'undefined'){ - const currentHost = window.location.host; - - window.open(link, '_blank'); - } - } - - function formatDate(dateString) { - console.log("date", date) - const inputDate = new Date(dateString); - const now = new Date(); - const oneDay = 24 * 60 * 60 * 1000; - - if (now - inputDate < oneDay) { - const hours = inputDate.getHours().toString().padStart(2, '0'); - const minutes = inputDate.getMinutes().toString().padStart(2, '0'); - return `Today at ${hours}:${minutes}`; - } else { - - const options = { year: 'numeric', month: 'long', day: 'numeric' }; - return inputDate.toLocaleDateString(undefined, options); - } - } - -return ( -
-

- {title}
-

- -
- Project Image -
- -

{desc}

- {(slack ? (slack.startsWith('@') ? (slack) : (`@${slack}`)) : (""))+ " "} - {formatDate(date)} - - -
- ) -} - -export default BinPost; \ No newline at end of file diff --git a/pages/api/bin/gallery/tags.js b/pages/api/bin/gallery/tags.js new file mode 100644 index 00000000..cc775954 --- /dev/null +++ b/pages/api/bin/gallery/tags.js @@ -0,0 +1,35 @@ +import AirtablePlus from "airtable-plus"; + +const fetchTags = async () => { + try { + const airtable = new AirtablePlus({ + apiKey: process.env.AIRTABLE_API_KEY, + baseID: 'appKjALSnOoA0EmPk', + tableName: 'Supported Parts', + }); + + const records = await airtable.read(); + + const tags = records.map((record) => { + return { + ID: record.id, + hide: record.fields["Hide From Gallery"], + }; + }); + + console.log('tags', tags); + return tags; + } catch (error) { + console.error('Error fetching tags:', error); + throw error; + } +}; + +export default async function handler(req, res) { + try { + const data = await fetchTags(); + res.status(200).json(data); + } catch (error) { + res.status(500).json({ error: 'Failed to fetch tags' }); + } +} diff --git a/pages/bin/gallery.js b/pages/bin/gallery.js index 652097bf..d81fbd75 100644 --- a/pages/bin/gallery.js +++ b/pages/bin/gallery.js @@ -2,7 +2,8 @@ import React from 'react' import BinPost from '../../components/bin/GalleryPosts' import styles from '../../public/bin/style/gallery.module.css' import Nav from '../../components/bin/nav' -import Footer from '../../components/bin/footer' +import Footer from '../../components/footer' +import PartTag from '../../components/bin/PartTag'; import { useEffect, useRef, useState } from 'react'; import { resolve } from 'styled-jsx/css'; import { set } from 'lodash'; @@ -12,30 +13,97 @@ export async function getStaticProps() { const res = await fetch(`${host}/api/bin/gallery/posts/`); const posts = await res.json(); - const filteredPosts = posts.filter(post => post.status === 'Accepted'); + const filteredPosts = posts.filter(post => post.status === 'Accepted' && post.parts && !post.hide); + + //Tags + + const resTag = await fetch(`${host}/api/bin/gallery/tags/`); + const tags = await resTag.json(); + + const filteredTags = tags.filter(tag => !tag.hide); return { - props: { posts: filteredPosts }, + props: { posts: filteredPosts, + tags: filteredTags + }, }; } -function Gallery({ posts = [] }) { + +function Gallery({ posts = [], tags = [] }) { + + const [allPosts, setAllPosts] = useState([]); + const [filterPosts, setFilterPosts] = useState([]); + const [filterParts, setFilterParts] = useState([]); + + useEffect(() => { + setAllPosts(posts); + setFilterParts([]); + + }, []); + + useEffect(() => { + setFilterPosts( + allPosts.filter(post => + post.parts && filterParts.every(part => post.parts.includes(part)) + ) + ); + }, [filterParts]); + + const addFilter = (partID) => { + setFilterParts((prevParts) => { + if (!prevParts.includes(partID)) { + console.log("add", partID) + return [...prevParts, partID]; + } + return prevParts; + }); + + }; + + const removeFilter = (partID) => { + setFilterParts((prevParts) => { + return prevParts.filter(id => id !== partID); + }); + }; + return (
-