diff --git a/README.md b/README.md index 865fbb1..f25363d 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,41 @@ -# Open-Source-Recommender -As a developer👨‍💻, I faced many problems in finding open-source projects🤷‍♂️. Don't worry! I brought you guys an application to find your open-source project by just typing in your GitHub username.✌️ -The application recommends Open Source Repositories based on your interests and languages that interest you. +![alt text](public/application.png) -## Features🤖 +# open-source-recommender -- Retrieves repository details of a GitHub user, including project names, descriptions, programming languages, and topics. -- Searches and gathers open source projects based on user language and topic interests using the GitHub API. -- Generates recommendations by comparing user repository details with the collected open-source projects. -- Provides a web interface using Streamlit to input user information and display recommended projects with link previews. +This will be a free public-facing web application designed to find open-source projects for beginners and developers. +Search your next contribution to open source easily! A free web app is here to help every developer find cool open-source projects of interest that fit their skillset. Just enter your GitHub username; our intelligent recommender system will do the rest. +## Why Use Open-Source Project Finder? +- **Perfect for Beginners**: Jump into open source with projects perfect for your current skill level. +- **Tailored Recommendations**: Recommendations on projects based on your GitHub profile, preferred languages and interests. +- **Expand Your Horizons**: New technologies, new projects you never would have crossed. +- **Absolutely Free**: Just free – gift to the developer community. -https://github.com/Hk669/Open-Source-Recommender/assets/96101829/eb45c74f-82ee-4c61-91f5-468d352084d8 +## How It Works +1. Input your GitHub username +2. Indicate your preferred programming languages and topics of Interest *Optional* +3. Get a personalized list of open source projects matching your profile *Optional* +the app analyzes your Github repositories against the vast database of open source projects from github to provide the best recommended match. +## Features +- **User-Friendly Interface:** Clean, intuitive design for seamless performance. +- **GitHub Integration**: Bases Users and Projects on the GitHub API for proper user data and details of projects +- **Smart Recommendations**: It fits appropriate projects to each user using robust algorithms. +- **Diverse Project Pool**: Multiple projects from different domains and projects which are of any difficulty level +- **Quick Access**: There are multiple direct links to recommended projects to access them fast. +## Get Started -## Installation +**still under construction* -1. Clone the repository: +## Feedback - ``` shell - git clone https://github.com/Hk669/Open-Source-Recommender.git - - ``` - -2. Install the required dependencies: - - ```shell - pip install -r requirements.txt - ``` - -3. Obtain a GitHub Personal Access Token (PAT): - - Visit https://github.com/settings/tokens and generate a new token with the necessary permissions. - - Copy the token and update the `ACCESS_TOKEN` variable in the `user.py` file with your token. - -## Contributions -- Contributions to the Open Source recommender project are welcome. If you find any bugs, have suggestions for improvements, or want to add new features, please submit an issue or create a pull request. +If you have any suggestions, find any bugs, or have success stories you'd like to share with me, please do so. Your input will make the experience better for all. ## License diff --git a/chroma/2fb656f8-eea6-4410-9c54-1e6d21ab400c/data_level0.bin b/chroma/2fb656f8-eea6-4410-9c54-1e6d21ab400c/data_level0.bin new file mode 100644 index 0000000..ae2f009 Binary files /dev/null and b/chroma/2fb656f8-eea6-4410-9c54-1e6d21ab400c/data_level0.bin differ diff --git a/chroma/2fb656f8-eea6-4410-9c54-1e6d21ab400c/header.bin b/chroma/2fb656f8-eea6-4410-9c54-1e6d21ab400c/header.bin new file mode 100644 index 0000000..d2383e3 Binary files /dev/null and b/chroma/2fb656f8-eea6-4410-9c54-1e6d21ab400c/header.bin differ diff --git a/chroma/2fb656f8-eea6-4410-9c54-1e6d21ab400c/length.bin b/chroma/2fb656f8-eea6-4410-9c54-1e6d21ab400c/length.bin new file mode 100644 index 0000000..eb086ff Binary files /dev/null and b/chroma/2fb656f8-eea6-4410-9c54-1e6d21ab400c/length.bin differ diff --git a/chroma/2fb656f8-eea6-4410-9c54-1e6d21ab400c/link_lists.bin b/chroma/2fb656f8-eea6-4410-9c54-1e6d21ab400c/link_lists.bin new file mode 100644 index 0000000..e69de29 diff --git a/chroma/chroma.sqlite3 b/chroma/chroma.sqlite3 new file mode 100644 index 0000000..35160d3 Binary files /dev/null and b/chroma/chroma.sqlite3 differ diff --git a/client/package-lock.json b/client/package-lock.json index 628a141..c9fc96c 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -12,6 +12,7 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "axios": "^1.7.2", + "hamburger-react": "^2.5.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-scripts": "5.0.1", @@ -9077,6 +9078,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/hamburger-react": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/hamburger-react/-/hamburger-react-2.5.1.tgz", + "integrity": "sha512-XlTIinYeYzLu76q4Vd9olwOJP0hFgAeZfJFX6tTT/ufTLhmOjI77CGFYIwGc6gcDeeT86660ZoKx3/L67vdZEw==", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18" + } + }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", diff --git a/client/package.json b/client/package.json index 4458b3a..5f55d55 100644 --- a/client/package.json +++ b/client/package.json @@ -7,6 +7,7 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "axios": "^1.7.2", + "hamburger-react": "^2.5.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-scripts": "5.0.1", diff --git a/client/src/App.css b/client/src/App.css index ad7e278..e30c0fb 100644 --- a/client/src/App.css +++ b/client/src/App.css @@ -3,11 +3,16 @@ body { font-family: 'Inter', sans-serif; background-color: #ffffff; - color: #333; + color: #000000; margin: 0; padding: 0; } +.content-container { + width: 70%; + margin: 0 auto; +} + .App { align-items: center; justify-content: center; @@ -41,89 +46,55 @@ body { } -form { - background: white; - padding: 20px; - border-radius: 10px; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); - width: 100%; - max-width: 500px; -} - -form label { - display: block; - font-weight: bold; - margin-bottom: 10px; - color: #444; -} -form input { - width: 100%; - padding: 10px; - margin-bottom: 15px; - border: 1px solid #ccc; - border-radius: 5px; - font-size: 1rem; -} - -form button { - width: 100%; - padding: 10px; - background-color: #007bff; - border: none; - border-radius: 5px; - color: white; - font-size: 1rem; - cursor: pointer; - transition: background-color 0.3s; +@media (max-width: 600px) { + .App-header h1 { + font-size: 1.5rem; + } } -form button:hover { - background-color: #0056b3; +.app-container { + display: flex; + justify-content: space-between; + width: 100%; + max-width: 1200px; + margin: 0 auto; + padding: 20px; } -h2 { - font-size: 1.5rem; - margin-top: 30px; - margin-bottom: 10px; - color: #555; +.form-container { + width: 100%; + transition: width 0.3s ease-in-out; } -ul { - list-style-type: none; - padding: 0; - width: 100%; - max-width: 500px; - background: white; - border-radius: 10px; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); - margin-bottom: 20px; +.form-container.with-recommendations { + width: 50%; } -ul li { - padding: 10px; - border-bottom: 1px solid #ddd; +.recommendations-container { + width: 45%; + opacity: 0; + transition: opacity 0.3s ease-in-out; } -ul li:last-child { - border-bottom: none; +.recommendations-container.visible { + opacity: 1; } -ul li a { - text-decoration: none; - color: #007bff; -} +/* ... (rest of your CSS remains the same) ... */ -ul li a:hover { - text-decoration: underline; -} +@media (max-width: 900px) { + .app-container { + flex-direction: column; + } -@media (max-width: 600px) { - .App-header h1 { - font-size: 1.5rem; - } + .form-container, + .form-container.with-recommendations, + .recommendations-container { + width: 100%; + } - form, ul { - width: 90%; - } + .recommendations-container { + margin-top: 20px; + } } diff --git a/client/src/App.js b/client/src/App.js index d8f37db..d4431ff 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -4,38 +4,52 @@ import Input from "./components/Input/Input"; import { ToastContainer } from "react-toastify"; import "react-toastify/dist/ReactToastify.css"; import { Recommendation } from "./components/Recommendation/Recommendation"; -import { Navbar } from "./components/Navbar/Navbar"; +import Navbar from "./components/Navbar/Navbar"; + function App() { const [recommendations, setRecommendations] = useState([]); const [loading, setLoading] = useState(false); - const handleSubmit = async (recommendations) => { + const handleSubmit = async (inputData) => { setLoading(true); - console.log("recommendations"); - console.log(recommendations); - - setRecommendations(recommendations); - setLoading(false); + try { + setRecommendations(inputData); + } catch (error) { + console.error("Error fetching recommendations:", error); + // You might want to show an error message to the user here + } finally { + setLoading(false); + } }; return (
-
-
+
+
0 ? "with-recommendations" : "" + }`} + >
- {loading ? ( -

Loading recommendations...

- ) : recommendations.length > 0 ? ( -
+
0 ? "visible" : "" + }`} + > + {loading ? ( +
+
+

Loading recommendations...

+
+ ) : recommendations.length > 0 ? ( -
- ) - : null} - + ) : null} +
+
); } diff --git a/client/src/assets/logo.png b/client/src/assets/logo.png new file mode 100644 index 0000000..2d29945 Binary files /dev/null and b/client/src/assets/logo.png differ diff --git a/client/src/components/Input/Input.css b/client/src/components/Input/Input.css index e69de29..60eeee2 100644 --- a/client/src/components/Input/Input.css +++ b/client/src/components/Input/Input.css @@ -0,0 +1,112 @@ +form { + background: white; + padding: 20px; + border-radius: 10px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + width: 100%; + max-width: 600px; + margin: 0 auto; +} + + +form label { + display: block; + font-weight: bold; + margin-bottom: 10px; + color: #444; + padding-bottom: 5px; +} + +form input { + width: 100%; + padding: 10px; + margin-bottom: 15px; + border: 1px solid #ccc; + border-radius: 5px; + font-size: 1rem; +} + +form button { + width: 100%; + padding: 10px; + background-color: #000000; + border: none; + border-radius: 5px; + color: white; + font-size: 1rem; + cursor: pointer; + transition: background-color 0.3s; +} + +form button:hover { + background-color: #ffffff; + color:#000000; +} + +h2 { + font-size: 1.5rem; + margin-top: 30px; + margin-bottom: 10px; + color: #555; +} + +ul { + list-style-type: none; + padding: 0; + width: 100%; + max-width: 500px; + background: white; + border-radius: 10px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + margin-bottom: 20px; +} + +ul li { + padding: 10px; + border-bottom: 1px solid #ddd; +} + +ul li:last-child { + border-bottom: none; +} + +ul li a { + text-decoration: none; + color: #007bff; +} + +ul li a:hover { + text-decoration: underline; +} + +@media (max-width: 600px) { + + form, ul { + width: 90%; + } +} + +.tag-container { + display: flex; + flex-wrap: wrap; + margin-bottom: 5px; +} + +.tag { + background-color: #e0e0e0; + border-radius: 16px; + padding: 5px 10px; + margin-right: 5px; + margin-bottom: 5px; + display: flex; + align-items: center; +} + +.tag button { + background: none; + border: none; + cursor: pointer; + font-size: 16px; + margin-left: 5px; + padding: 0; +} \ No newline at end of file diff --git a/client/src/components/Input/Input.jsx b/client/src/components/Input/Input.jsx index 4ce3265..235e6b8 100644 --- a/client/src/components/Input/Input.jsx +++ b/client/src/components/Input/Input.jsx @@ -3,11 +3,12 @@ import axios from "axios"; import "./Input.css"; import { toast } from "react-toastify"; - const Input = ({ onSubmit }) => { const [username, setUsername] = useState(""); - const [languages, setLanguages] = useState(""); - const [extraTopics, setExtraTopics] = useState(""); + const [languages, setLanguages] = useState([]); + const [extraTopics, setExtraTopics] = useState([]); + const [languageInput, setLanguageInput] = useState(""); + const [extraTopicInput, setExtraTopicInput] = useState(""); const handleSubmit = async (e) => { e.preventDefault(); @@ -16,26 +17,21 @@ const Input = ({ onSubmit }) => { "http://127.0.0.1:8000/api/recommendations/", { username, - languages: languages.split(",").map((item) => item.trim()), - extra_topics: extraTopics.split(",").map((item) => item.trim()), + languages, + extra_topics: extraTopics, } ); - console.log(languages) - console.log(extraTopics) onSubmit(response.data.recommendations); } catch (error) { if (error.response) { - // Errors from the server toast.error(`${error.response.data.detail}`, { position: "top-right", }); } else if (error.request) { - // Network errors toast.error("Network error. Please try again later.", { position: "top-right", }); } else { - // Other errors toast.error(`An unexpected error occurred: ${error.message}`, { position: "top-right", }); @@ -43,6 +39,52 @@ const Input = ({ onSubmit }) => { } }; + const handleLanguageInputChange = (e) => { + setLanguageInput(e.target.value); + }; + + const handleExtraTopicInputChange = (e) => { + setExtraTopicInput(e.target.value); + }; + + const handleLanguageInputKeyDown = (e) => { + if (e.key === "Enter" || e.key === ",") { + e.preventDefault(); + addLanguage(); + } + }; + + const handleExtraTopicInputKeyDown = (e) => { + if (e.key === "Enter" || e.key === ",") { + e.preventDefault(); + addExtraTopic(); + } + }; + + const addLanguage = () => { + const trimmedInput = languageInput.trim(); + if (trimmedInput && !languages.includes(trimmedInput)) { + setLanguages([...languages, trimmedInput]); + setLanguageInput(""); + } + }; + + const addExtraTopic = () => { + const trimmedInput = extraTopicInput.trim(); + if (trimmedInput && !extraTopics.includes(trimmedInput)) { + setExtraTopics([...extraTopics, trimmedInput]); + setExtraTopicInput(""); + } + }; + + const removeLanguage = (language) => { + setLanguages(languages.filter((lang) => lang !== language)); + }; + + const removeExtraTopic = (topic) => { + setExtraTopics(extraTopics.filter((t) => t !== topic)); + }; + return (
diff --git a/client/src/components/Navbar/Navbar.css b/client/src/components/Navbar/Navbar.css index 9f1af32..7fce1b5 100644 --- a/client/src/components/Navbar/Navbar.css +++ b/client/src/components/Navbar/Navbar.css @@ -1,34 +1,40 @@ .navbar { - background-color: #ffffff; - color: #000000; + background-color: white; + width: 100%; + display: flex; + justify-content: center; +} + +.navbar-content { + width: 70%; display: flex; justify-content: space-between; align-items: center; - padding: 10px 20px; + padding: 1rem 0; } -.logo { - font-size: 1.5rem; - font-weight: bold; +.logo img { + height: 40px; /* Adjust as needed */ } .nav-links { - display: flex; - border: none; - box-shadow: none; -} - -.nav-links li { - margin-right: 20px; + list-style-type: none; + padding: 0; + margin: 0; } .nav-links li a { - color: #000000; text-decoration: none; - transition: color 0.3s ease; + color: #000000; /* Adjust color as needed */ + font-weight: bold; } .nav-links li a:hover { - color: #dcb8e0; text-decoration: none; -} + background: linear-gradient(to right, #9181f4 -40%, #ed6c7d 140%); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + /* font-weight: 700; */ + cursor: pointer; +} \ No newline at end of file diff --git a/client/src/components/Navbar/Navbar.jsx b/client/src/components/Navbar/Navbar.jsx index 9d0caf8..e2c58cb 100644 --- a/client/src/components/Navbar/Navbar.jsx +++ b/client/src/components/Navbar/Navbar.jsx @@ -1,29 +1,24 @@ import React from "react"; -import "./Navbar.css"; // Import CSS file for styling +import logo from "../../assets/logo.png"; +import "./Navbar.css"; // We'll create this file for styling -export const Navbar = () => { +function Navbar() { return ( -
-
Logo
- -
+
+ {/* */} +
+ ); -}; +} +export default Navbar; diff --git a/client/src/components/Recommendation/Recommendation.css b/client/src/components/Recommendation/Recommendation.css index 8e5bf42..b7f3efd 100644 --- a/client/src/components/Recommendation/Recommendation.css +++ b/client/src/components/Recommendation/Recommendation.css @@ -6,6 +6,16 @@ margin-top: 20px; } +.reco-container ul li:hover { + text-decoration: none; + background: linear-gradient(to right, #9181f4 -40%, #ed6c7d 140%); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + font-weight: 700; + cursor: pointer; +} + .reco-container ul { list-style-type: none; padding: 0; @@ -22,5 +32,5 @@ } .reco-container ul li a:hover { - text-decoration: underline; + text-decoration: none; } diff --git a/client/src/index.css b/client/src/index.css index ec2585e..228b82a 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -1,13 +1,29 @@ -body { +@import url('https://fonts.googleapis.com/css2?family=Inter&display=swap'); + +:root { + --c-b: #090909; + --c-w: #ffffff; + --c-g: rgba(0, 0, 0, 0.53); + /* --f-p: 'Roboto', sans-serif; */ +} + +* { margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + padding: 0; + box-sizing: border-box; + font-family: 'Inter', sans-serif; + scroll-behavior: smooth; } -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; +body { + overflow-x: hidden; } + +.app { + width: 100%; + max-width: 100%; + height: 100%; + padding: 1.4rem 0 0; + overflow-x: hidden; + background-color: var(--c-b); +} \ No newline at end of file diff --git a/public/application.png b/public/application.png new file mode 100644 index 0000000..f93c646 Binary files /dev/null and b/public/application.png differ