diff --git a/package-lock.json b/package-lock.json index eadb7c4..3156ff7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "0.0.0", "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^7.10.1" }, "devDependencies": { "@types/react": "^18.0.37", @@ -1505,6 +1506,19 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3555,6 +3569,44 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.10.1.tgz", + "integrity": "sha512-gHL89dRa3kwlUYtRQ+m8NmxGI6CgqN+k4XyGjwcFoQwwCWF6xXpOCUlDovkXClS0d0XJN/5q7kc5W3kiFEd0Yw==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.10.1.tgz", + "integrity": "sha512-JNBANI6ChGVjA5bwsUIwJk7LHKmqB4JYnYfzFwyp2t12Izva11elds2jx7Yfoup2zssedntwU0oZ5DEmk5Sdaw==", + "license": "MIT", + "dependencies": { + "react-router": "7.10.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", @@ -3723,6 +3775,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5464,6 +5522,11 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, + "cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==" + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -6960,6 +7023,23 @@ "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", "dev": true }, + "react-router": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.10.1.tgz", + "integrity": "sha512-gHL89dRa3kwlUYtRQ+m8NmxGI6CgqN+k4XyGjwcFoQwwCWF6xXpOCUlDovkXClS0d0XJN/5q7kc5W3kiFEd0Yw==", + "requires": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + } + }, + "react-router-dom": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.10.1.tgz", + "integrity": "sha512-JNBANI6ChGVjA5bwsUIwJk7LHKmqB4JYnYfzFwyp2t12Izva11elds2jx7Yfoup2zssedntwU0oZ5DEmk5Sdaw==", + "requires": { + "react-router": "7.10.1" + } + }, "regexp.prototype.flags": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", @@ -7073,6 +7153,11 @@ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, + "set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==" + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", diff --git a/package.json b/package.json index 727f1c1..52955cc 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ }, "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^7.10.1" }, "devDependencies": { "@types/react": "^18.0.37", diff --git a/src/App.css b/src/App.css index b9d355d..522dd4d 100644 --- a/src/App.css +++ b/src/App.css @@ -1,42 +1,352 @@ -#root { - max-width: 1280px; +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + 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; + background-color: #f5f5f5; +} + +/* App Component */ +.App { + min-height: 100vh; +} + +/* Navbar */ +nav { + background-color: #6c63ff; + padding: 20px 40px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +nav h1 { + color: white; + font-size: 2rem; + font-weight: 700; + margin-bottom: 10px; +} + +nav p { + color: white; + font-size: 1.5rem; + font-weight: 600; + margin: 0; +} + +/* HomePage */ +.HomePage { + max-width: 1200px; + margin: 0 auto; + padding: 60px 40px; +} + +.HomePage h2 { + text-align: center; + color: #000; + margin-bottom: 60px; + font-size: 1.8rem; + font-weight: 600; +} + +.companies-list { + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 40px; + justify-content: center; + max-width: 900px; + margin: 0 auto; +} + +.company-hero { + display: flex; + flex-direction: row; + gap: 20px; +} + +.company-info { + flex: 3; +} + +.company-card { + background: white; + padding: 50px; + border-radius: 8px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + text-align: center; + transition: box-shadow 0.3s ease; + cursor: pointer; + border: 1px solid #e0e0e0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + flex: 0 0 calc(50% - 20px); + min-width: 300px; +} + +.company-card:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); +} + +.company-card img { + width: 120px; + height: 120px; + object-fit: contain; + margin-bottom: 20px; +} + +.company-card p { + color: #000; + font-size: 1.1rem; + font-weight: 600; + margin: 0; +} + +a { + text-decoration: none; + color: inherit; +} + +/* CompanyPage */ +.CompanyPage { + max-width: 1100px; + margin: 0 auto; + padding: 60px 40px; +} + +.CompanyPage-content { + display: flex; + gap: 60px; + align-items: start; + margin-bottom: 40px; +} + +.CompanyPage h2 { + color: #000; + text-align: center; + font-size: 1.5rem; + font-weight: 600; + margin-bottom: 40px; +} + +.company-logo-container { + flex: 0 0 auto; +} + +.CompanyPage img { + width: 200px; + height: 200px; + object-fit: contain; + background: white; + padding: 40px; + border-radius: 8px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + border: 1px solid #e0e0e0; +} + +.company-info { + flex: 1; + display: flex; + flex-direction: column; +} + +.CompanyPage h3 { + color: #000; + font-size: 2.5rem; + font-weight: 700; + margin-bottom: 20px; +} + +.CompanyPage h4 { + color: #000; + font-size: 1.1rem; + font-weight: 700; + margin-bottom: 10px; +} + +.CompanyPage p { + color: #333; + line-height: 1.8; + margin-bottom: 20px; + font-size: 1rem; + text-align: left; +} + +.CompanyPage a { + color: #6c63ff; + text-decoration: none; +} + +.CompanyPage a:hover { + text-decoration: underline; +} + +/* Tech Stack */ +.tech-stack { + display: flex; + flex-wrap: wrap; + gap: 20px; + margin-top: 20px; +} + +.tech-card { + background: white; + padding: 30px 20px; + border-radius: 8px; + text-align: center; + transition: box-shadow 0.3s ease; + cursor: pointer; + border: 1px solid #e0e0e0; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + flex: 0 0 140px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.tech-card:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); +} + +.tech-card img { + width: 80px; + height: 80px; + object-fit: contain; + margin-bottom: 15px; +} + +.tech-card p { + color: #000; + font-size: 0.95rem; + font-weight: 600; + margin: 0; +} + +/* TechnologyPage */ +.TechnologyPage { + max-width: 900px; margin: 0 auto; - padding: 2rem; + padding: 60px 40px; + display: flex; + justify-content: center; text-align: center; + flex-direction: column; } -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; +.TechnologyPage h2 { + color: #000; + margin-bottom: 40px; + text-align: center; + font-size: 1.5rem; + font-weight: 600; } -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); + +.TechnologyPage > img { + display: block; + width: 150px; + height: 150px; + object-fit: contain; + margin: 0 auto 20px; + background: white; + padding: 25px; + border-radius: 8px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + border: 1px solid #e0e0e0; } -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); + +.TechnologyPage h3 { + color: #000; + font-size: 2rem; + font-weight: 700; + text-align: center; + margin-bottom: 20px; +} + +.TechnologyPage p { + color: #333; + line-height: 1.8; + margin-bottom: 20px; + font-size: 1rem; + text-align: center; +} + +/* Buttons */ +button { + background-color: #6c63ff; + color: white; + border: none; + padding: 12px 30px; + font-size: 1rem; + border-radius: 5px; + cursor: pointer; + transition: background-color 0.3s ease; + margin-top: 20px; + font-weight: 500; } -@keyframes logo-spin { - from { - transform: rotate(0deg); +button:hover { + background-color: #5a52d5; +} + +/* Responsive Design */ +@media (max-width: 992px) { + .CompanyPage-content { + flex-direction: column; + align-items: center; + text-align: center; + } + + .company-info { + align-items: center; } - to { - transform: rotate(360deg); + + .CompanyPage p { + text-align: center; } -} -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; + .tech-stack { + justify-content: center; } } -.card { - padding: 2em; +@media (max-width: 768px) { + .companies-list { + flex-direction: column; + align-items: stretch; + } + + .company-card { + flex: 1 1 auto; + min-width: auto; + } + + .tech-stack { + gap: 15px; + } + + .tech-card { + flex: 0 0 120px; + } + + .CompanyPage h3 { + font-size: 2rem; + } } -.read-the-docs { - color: #888; +@media (max-width: 480px) { + .tech-stack { + justify-content: space-between; + } + + .tech-card { + flex: 0 0 calc(50% - 10px); + } } diff --git a/src/App.jsx b/src/App.jsx index 1b51d87..b34a860 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,10 +1,34 @@ import "./App.css"; +import { Routes, Route } from "react-router-dom"; +import { useState } from "react"; + +import HomePage from "./pages/HomePage.jsx"; +import CompanyPage from "./pages/CompanyPage.jsx"; +import TechnologyPage from "./pages/TechnologyPage.jsx"; +import Navbar from "./components/Navbar.jsx"; + +import companyData from "./data/companies.json"; +import technologyData from "./data/technologies.json"; + function App() { + const [companies, setCompanies] = useState(companyData); + const [technologies, setTechnologies] = useState(technologyData); return ( -
-

LAB | React Stack Tracker

-
+ <> + + + } /> + } + /> + } + /> + + ); } diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx index 09e2806..d301559 100644 --- a/src/components/Navbar.jsx +++ b/src/components/Navbar.jsx @@ -1,5 +1,13 @@ +import { Link } from "react-router-dom"; + function Navbar() { - return ; + return ( + + ); } export default Navbar; diff --git a/src/companies.json b/src/data/companies.json similarity index 100% rename from src/companies.json rename to src/data/companies.json diff --git a/src/technologies.json b/src/data/technologies.json similarity index 100% rename from src/technologies.json rename to src/data/technologies.json diff --git a/src/index.css b/src/index.css index b121ef9..154314c 100644 --- a/src/index.css +++ b/src/index.css @@ -1,4 +1,4 @@ -:root { +/* :root { font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; @@ -61,4 +61,4 @@ button:focus-visible { button { background-color: #f9f9f9; } -} +} */ diff --git a/src/main.jsx b/src/main.jsx index 54b39dd..53f68c8 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,10 +1,13 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App.jsx' -import './index.css' +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App.jsx"; +import "./index.css"; +import { BrowserRouter as Router } from "react-router-dom"; -ReactDOM.createRoot(document.getElementById('root')).render( +ReactDOM.createRoot(document.getElementById("root")).render( - - , -) + + + + +); diff --git a/src/pages/CompanyPage.jsx b/src/pages/CompanyPage.jsx index cc5903c..3090ec3 100644 --- a/src/pages/CompanyPage.jsx +++ b/src/pages/CompanyPage.jsx @@ -1,7 +1,42 @@ -function CompanyPage() { +import { useParams, Link } from "react-router-dom"; + +function CompanyPage({ companies }) { + const { companySlug } = useParams(); + + const company = companies.find((c) => c.slug === companySlug); + + if (!company) { + return
Company not found
; + } + return ( -
-

CompanyPage

+
+

Company Profile

+
+ {`${company.name} +
+

{company.name}

+

+ Website:{" "} + + {company.website} + {" "} +

+

{company.description}

+
+
+ +

Tech Stack:

+
+ {company.techStack.map((tech, index) => ( + +
+ {`${tech.name} +

{tech.name}

+
+ + ))} +
); } diff --git a/src/pages/HomePage.jsx b/src/pages/HomePage.jsx index e97e7de..feb58ae 100644 --- a/src/pages/HomePage.jsx +++ b/src/pages/HomePage.jsx @@ -1,7 +1,19 @@ -function HomePage() { +import { Link } from "react-router-dom"; + +function HomePage({ companies }) { return ( -
-

HomePage

+
+

StackTracker: Discover Tech Stacks Used by Top Companies

+
+ {companies.map((company) => ( + +
+ {`${company.name} +

{company.name}

+
+ + ))} +
); } diff --git a/src/pages/TechnologyPage.jsx b/src/pages/TechnologyPage.jsx index 30f9414..5065bc7 100644 --- a/src/pages/TechnologyPage.jsx +++ b/src/pages/TechnologyPage.jsx @@ -1,7 +1,27 @@ -function TechnologyPage() { +import { useParams, Link, useSearchParams } from 'react-router-dom'; + +function TechnologyPage({ technologies }) { + const { techSlug } = useParams(); + const [searchParams] = useSearchParams(); + const companySlug = searchParams.get('company'); + const technology = technologies.find((tech) => tech.slug === techSlug); + + if (!technology) { + return
Technology not found
; + } + return ( -
-

TechnologyPage

+
+

Technology Details

+ {`${technology.name} +

{technology.name}

+

{technology.description}

+ + {companySlug && ( + + + + )}
); }