diff --git a/.gitignore b/.gitignore
index b512c09..5287d85 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
-node_modules
\ No newline at end of file
+node_modules
+env
+.env
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index cc7cb98..1e8c8ec 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4513,9 +4513,9 @@
}
},
"dotenv": {
- "version": "10.0.0",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
- "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q=="
+ "version": "16.4.7",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
+ "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="
},
"dotenv-expand": {
"version": "5.1.0",
@@ -10368,6 +10368,13 @@
"webpack-dev-server": "^4.6.0",
"webpack-manifest-plugin": "^4.0.2",
"workbox-webpack-plugin": "^6.4.1"
+ },
+ "dependencies": {
+ "dotenv": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
+ "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q=="
+ }
}
},
"read-cache": {
diff --git a/package.json b/package.json
index d006183..97e4a66 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
+ "dotenv": "^16.4.7",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1"
diff --git a/src/App.js b/src/App.js
index 1462546..10a2aa2 100644
--- a/src/App.js
+++ b/src/App.js
@@ -6,57 +6,55 @@ import ProjectDetails from './components/ProjectDetails';
import projects from './data/projects';
function App() {
- const [selectedProjectId, setSelectedProjectId] = useState(null);
-
- const handleProjectClick = (projectId) => {
- setSelectedProjectId(projectId);
- window.scrollTo(0, 0);
- };
-
- const handleBackClick = () => {
- setSelectedProjectId(null);
- };
-
- const selectedProject = selectedProjectId
- ? projects.find(p => p.id === selectedProjectId)
- : null;
+ const [selectedProjectId, setSelectedProjectId] = useState(null);
- return (
-
-
-
-
- {selectedProject ? (
-
- ) : (
-
-
-
Freelance Experience
-
- Below is a portfolio of my freelance projects.
- Each project represents solutions to complex problems across various domains. If you've been given this site please feel free to contact me for more details that can't be made publicly available.
-
-
+ const handleProjectClick = (projectId) => {
+ setSelectedProjectId(projectId);
+ window.scrollTo(0, 0);
+ };
+
+ const handleBackClick = () => {
+ setSelectedProjectId(null);
+ };
+
+ const selectedProject = selectedProjectId ? projects.find(p => p.id === selectedProjectId) : null;
+
+ return (
+
+
- {projects.map(project => (
-
- ))}
-
- )}
-
-
-
-
- );
+
+ {selectedProject ? (
+
+ ) : (
+
+
+
Freelance Experience
+
+ Below is a portfolio of my freelance projects.
+ Each project represents solutions to complex problems across various domains. If you've been given this site please feel free to contact me for more details that can't be made publicly available.
+
+
+
+ {projects.map(project => (
+
+ ))}
+
+ )}
+
+
+
+
+ );
}
export default App;
\ No newline at end of file
diff --git a/src/components/Header.js b/src/components/Header.js
index 8158f52..8a4ccce 100644
--- a/src/components/Header.js
+++ b/src/components/Header.js
@@ -1,19 +1,19 @@
import React from 'react';
const Header = ({ onBackClick, showBackButton }) => {
- return (
-
-
- {showBackButton && (
-
- ← Back to Projects
-
- )}
-
Comet Technologies
-
You need I'll build it
-
-
- );
+ return (
+
+
+ {showBackButton && (
+
+ ← Back to Projects
+
+ )}
+
Comet Technologies
+
You need I'll build it
+
+
+ );
};
export default Header;
\ No newline at end of file
diff --git a/src/components/ImageCarousel.js b/src/components/ImageCarousel.js
index 93b5258..16570a1 100644
--- a/src/components/ImageCarousel.js
+++ b/src/components/ImageCarousel.js
@@ -1,106 +1,166 @@
import React, { useState, useEffect } from 'react';
const ImageCarousel = ({ projectId }) => {
- const [images, setImages] = useState([]);
- const [currentImageIndex, setCurrentImageIndex] = useState(0);
- const [loading, setLoading] = useState(true);
-
- useEffect(() => {
+ const [images, setImages] = useState([]);
+ const [highResImages, setHighResImages] = useState([]);
+ const [fileIds, setFileIds] = useState([]);
+ const [currentImageIndex, setCurrentImageIndex] = useState(0);
+ const [loading, setLoading] = useState(true);
+ const [viewMode, setViewMode] = useState('thumbnail');
- const loadImages = async () => {
- try {
-
- const importAll = (r) => r.keys().map(r);
-
- let imageContext = null;
-
- try {
- switch(projectId){
- case "mycology-lab":
- imageContext = require.context(`../assets/mycology-lab`, false, /\.(png|jpe?g|svg)$/);
- break
- case "loanterra":
- imageContext = require.context(`../assets/loanterra`, false, /\.(png|jpe?g|svg)$/);
- break
- case "orchard-market":
- imageContext = require.context(`../assets/orchard-market`, false, /\.(png|jpe?g|svg)$/);
- break
- case "fecal-vision-model":
- imageContext = require.context(`../assets/fecal-vision-model`, false, /\.(png|jpe?g|svg)$/);
- break
- default:
- imageContext = require.context(`../assets/mycology-lab`, false, /\.(png|jpe?g|svg)$/);
- }
- } catch (e) {
- console.log("Error", e);
- }
- if(imageContext){
- const imageFiles = importAll(imageContext);
- setImages(imageFiles);
- }
- } catch (error) {
- console.error("Error loading images:", error);
- setImages([]);
- } finally {
- setLoading(false);
- }
+ const getDirectDownloadUrl = (fileId) => {
+ return `https://lh3.google.com/u/0/d/${fileId}`
};
- loadImages();
-
- setCurrentImageIndex(0);
- }, [projectId]);
+ const getThumbnailUrl = (fileId) => {
+ return `https://drive.google.com/thumbnail?id=${fileId}&sz=w300`;
+ };
- const nextImage = () => {
- setCurrentImageIndex((prevIndex) =>
- prevIndex === images.length - 1 ? 0 : prevIndex + 1
- );
- };
+ useEffect(() => {
+ const loadImages = async () => {
+ try {
+ const apiKey = process.env.REACT_APP_GOOGLE_API_KEY;
+
+ if (!apiKey) {
+ throw new Error("Google API key not found in environment variables");
+ }
- const prevImage = () => {
- setCurrentImageIndex((prevIndex) =>
- prevIndex === 0 ? images.length - 1 : prevIndex - 1
- );
- };
+ let folderId = "1kLdnnm47c7GkmKgiyIbv2QXh7sqFZ6p4";
+
+ let query = encodeURIComponent(`'${folderId}' in parents and trashed=false`);
- if (loading) {
- return Loading images...
;
- }
+ let driveResponse = await fetch(
+ `https://www.googleapis.com/drive/v3/files?q=${query}&fields=files(id,name,mimeType)&key=${apiKey}`
+ );
+
+ if (!driveResponse.ok) {
+ throw new Error(`Google Drive API error: ${driveResponse.status}`);
+ }
+
+ const folderData = await driveResponse.json();
+ let foundFolder = folderData.files.filter(file => file.mimeType.startsWith('application/')).find(item => item.name === projectId);
+
+ if (foundFolder) {
+ folderId = foundFolder.id;
+ query = encodeURIComponent(`'${folderId}' in parents and trashed=false`);
+ driveResponse = await fetch(
+ `https://www.googleapis.com/drive/v3/files?q=${query}&fields=files(id,name,mimeType)&key=${apiKey}`
+ );
+
+ const fileData = await driveResponse.json();
+ const ids = fileData.files
+ .filter(file => file.mimeType.startsWith('image/'))
+ .map(file => file.id);
+
+ setFileIds(ids);
+ const thumbnailUrls = ids.map(id => getThumbnailUrl(id));
+ setImages(thumbnailUrls);
+ }
+ } catch (error) {
+ console.error("Error loading images:", error);
+
+ setImages([]);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ loadImages();
+ setCurrentImageIndex(0);
+ setViewMode('thumbnail');
+ }, [projectId]);
- if (images.length === 0) {
- return No images available
;
- }
+ const toggleViewMode = () => {
+ if (viewMode === 'thumbnail') {
+ setViewMode('highres');
+ setHighResImages([]);
+ setLoading(true);
+ const highResUrls = fileIds.map(id => getDirectDownloadUrl(id));
+ setHighResImages(highResUrls);
+ setLoading(false);
+ } else {
+ setViewMode('thumbnail');
+
+ setImages([]);
+ setLoading(true);
+ const thumbnailUrls = fileIds.map(id => getThumbnailUrl(id));
+ setImages(thumbnailUrls);
+ setLoading(false);
+ }
+ };
- return (
-
-
- <
-
-
-
-
-
- {currentImageIndex + 1} / {images.length}
+ const nextImage = () => {
+ if(viewMode === 'highres')
+ toggleViewMode();
+ setCurrentImageIndex((prevIndex) =>
+ prevIndex === images.length - 1 ? 0 : prevIndex + 1
+ );
+ };
+
+ const prevImage = () => {
+ if(viewMode === 'highres')
+ toggleViewMode();
+ setCurrentImageIndex((prevIndex) =>
+ prevIndex === 0 ? images.length - 1 : prevIndex - 1
+ );
+ };
+
+ if (loading) {
+ return
Loading images...
;
+ }
+
+ if (images.length === 0) {
+ return
No images available
;
+ }
+ const addDefaultImg = ev => {
+ // ev.target.src = images[currentImageIndex]
+ ev.target.src = 'https://upload.wikimedia.org/wikipedia/commons/1/14/No_Image_Available.jpg'
+ }
+ return (
+
+
+ <
+
+
+
+
addDefaultImg}
+ style={{
+ width: '100%',
+ height: 'auto'
+ }}
+ />
+
+
+ {currentImageIndex + 1} / {images.length}
+ {viewMode === 'thumbnail' && (
+ [SD](Click Image for HD)
+ )}
+ {viewMode === 'highres' && (
+ [HD](Click Image for SD)
+ )}
+
+
+
+
+ >
+
-
-
-
- >
-
-
- );
+ );
};
+
+
export default ImageCarousel;
\ No newline at end of file
diff --git a/src/components/ProjectCard.js b/src/components/ProjectCard.js
index 3393b19..f022946 100644
--- a/src/components/ProjectCard.js
+++ b/src/components/ProjectCard.js
@@ -1,17 +1,17 @@
import React from 'react';
const ProjectCard = ({ project, onClick }) => {
- const summary = project.description.trim().split('\n\n')[0].trim();
- const summaryLength = 120;
- return (
-
onClick(project.id)}>
-
{project.title}
-
- {summary.length > summaryLength ? `${summary.substring(0, summaryLength)}...` : summary}
-
-
View Details
-
- );
+ const summary = project.description.trim().split('\n\n')[0].trim();
+ const summaryLength = 120;
+ return (
+
onClick(project.id)}>
+
{project.title}
+
+ {summary.length > summaryLength ? `${summary.substring(0, summaryLength)}...` : summary}
+
+
View Details
+
+ );
};
export default ProjectCard;
\ No newline at end of file
diff --git a/src/components/ProjectDetails.js b/src/components/ProjectDetails.js
index e770cc0..5575295 100644
--- a/src/components/ProjectDetails.js
+++ b/src/components/ProjectDetails.js
@@ -2,27 +2,27 @@ import React from 'react';
import ImageCarousel from './ImageCarousel';
const ProjectDetails = ({ project }) => {
- if (!project) {
- return
Project not found
;
- }
+ if (!project) {
+ return
Project not found
;
+ }
- const paragraphs = project.description.trim().split('\n\n');
+ const paragraphs = project.description.trim().split('\n\n');
- return (
-
-
{project.title}
-
-
-
-
-
-
- {paragraphs.map((paragraph, index) => (
-
{paragraph.trim()}
- ))}
-
-
- );
+ return (
+
+
{project.title}
+
+
+
+
+
+
+ {paragraphs.map((paragraph, index) => (
+
{paragraph.trim()}
+ ))}
+
+
+ );
};
export default ProjectDetails;
\ No newline at end of file