diff --git a/src/App.css b/src/App.css index 038db3e..7679040 100644 --- a/src/App.css +++ b/src/App.css @@ -24,13 +24,21 @@ } /* ---- Home Page ---- */ +.homePageShell { + min-height: 100vh; + display: flex; + flex-direction: column; + background: var(--bg-color); +} + .homePageWrapper { display: flex; justify-content: center; align-items: center; color: var(--text-color); - height: 100vh; + flex: 1; position: relative; + padding: 80px 20px 40px; } .formWrapper { @@ -160,6 +168,61 @@ background: var(--bg-color); } +.notFoundShell { + min-height: 100vh; + display: flex; + flex-direction: column; + background: var(--bg-color); +} + +.notFoundPage { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background-color: var(--bg-color); + color: var(--text-color); + font-family: Inter, sans-serif; + text-align: center; + padding: 20px; + transition: background 0.3s ease, color 0.3s ease; +} + +.notFoundCode { + font-size: 6rem; + margin: 0; + color: #4aed88; +} + +.notFoundTitle { + font-size: 1.8rem; + margin: 10px 0; + color: var(--text-color); +} + +.notFoundText { + color: var(--text-color); + opacity: 0.6; + margin-bottom: 30px; +} + +.notFoundButton { + background-color: #4aed88; + color: #1c1e29; + border: none; + padding: 12px 30px; + font-size: 1rem; + font-weight: bold; + border-radius: 8px; + cursor: pointer; + transition: background 0.2s ease; +} + +.notFoundButton:hover { + background-color: #2b824c; +} + /* ---- Theme Toggle (Sidebar pill switch) ---- */ .theme-toggle-aside { display: flex; @@ -1899,14 +1962,54 @@ body[data-theme='light'] .console-separator::after { } /* Footer */ -.landing-footer { - padding: 30px; - text-align: center; +.site-footer { border-top: 1px solid var(--border-color); - opacity: 0.7; + background: rgba(255, 255, 255, 0.02); +} + +.site-footer-inner { + max-width: 1120px; + margin: 0 auto; + padding: 24px 7%; + display: flex; + align-items: center; + justify-content: space-between; + gap: 18px; +} + +.site-footer-brand { + display: flex; + flex-direction: column; + gap: 8px; +} + +.site-footer-copy { + margin: 0; + opacity: 0.68; font-size: 0.9rem; } +.site-footer-nav { + display: flex; + align-items: center; + gap: 18px; + flex-wrap: wrap; +} + +.site-footer-nav a { + color: var(--text-color); + text-decoration: none; + opacity: 0.78; + font-size: 0.92rem; + font-weight: 600; + transition: color 0.2s ease, opacity 0.2s ease; +} + +.site-footer-nav a:hover { + color: #4aed88; + opacity: 1; +} + /* Responsive */ @media (max-width: 900px) { .landing-title { @@ -1920,6 +2023,15 @@ body[data-theme='light'] .console-separator::after { gap: 18px; } + .site-footer-inner { + flex-direction: column; + align-items: flex-start; + } + + .site-footer-nav { + width: 100%; + } + .landing-title { font-size: 2.4rem; } diff --git a/src/App.test.js b/src/App.test.js index b4ef4e2..adcd0ce 100644 --- a/src/App.test.js +++ b/src/App.test.js @@ -1,8 +1,9 @@ import { render, screen } from '@testing-library/react'; import App from './App'; -test('renders landing page call to action', () => { +test('renders landing page with footer navigation', () => { render(); - const buttonElement = screen.getByText(/Get Started/i); - expect(buttonElement).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /Get Started/i })).toBeInTheDocument(); + expect(screen.getByRole('link', { name: /Join Room/i })).toBeInTheDocument(); + expect(screen.getByRole('link', { name: /GitHub/i })).toBeInTheDocument(); }); diff --git a/src/components/SiteFooter.js b/src/components/SiteFooter.js new file mode 100644 index 0000000..34a320d --- /dev/null +++ b/src/components/SiteFooter.js @@ -0,0 +1,33 @@ +import { Link } from 'react-router-dom'; + +function SiteFooter({ showJoinLink = true }) { + return ( + + ); +} + +export default SiteFooter; diff --git a/src/pages/Home.js b/src/pages/Home.js index 76420ed..d464328 100644 --- a/src/pages/Home.js +++ b/src/pages/Home.js @@ -2,6 +2,7 @@ import { React, useState, useEffect } from "react"; import { v4 as uuid } from "uuid"; import toast from "react-hot-toast"; import { useNavigate, useSearchParams } from "react-router-dom"; +import SiteFooter from "../components/SiteFooter"; const Home = () => { const navigate = useNavigate(); @@ -41,62 +42,65 @@ const Home = () => { userName, } }); - } + }; const handleInputEnter=(e)=>{ if(e.code==='Enter'){ joinRoom(); } - } + }; return ( -
- -
- -
- CollabCE -
-

- {searchParams.get('roomId') ? '🎉 You were invited! Enter your username to join' : 'Paste Invitation Room Id'} -

-
- setRoomId(e.target.value)} - placeholder="ROOM ID" - onKeyUp={handleInputEnter} - /> - setUserName(e.target.value)} - className="inputBox" - placeholder="USERNAME" - onKeyUp={handleInputEnter} - /> - - - Want to create a new room? Click here{" "} - - New Room - - +
+ +
+ CollabCE +
+

+ {searchParams.get('roomId') ? '🎉 You were invited! Enter your username to join' : 'Paste Invitation Room Id'} +

+
+ setRoomId(e.target.value)} + placeholder="ROOM ID" + onKeyUp={handleInputEnter} + /> + setUserName(e.target.value)} + className="inputBox" + placeholder="USERNAME" + onKeyUp={handleInputEnter} + /> + + + Want to create a new room? Click here{" "} + + New Room + + +
+
); }; diff --git a/src/pages/Home.test.js b/src/pages/Home.test.js index 1a34c55..64191ff 100644 --- a/src/pages/Home.test.js +++ b/src/pages/Home.test.js @@ -19,3 +19,22 @@ test('navigates back to the landing page from the join screen', () => { expect(screen.getByText(/landing page/i)).toBeInTheDocument(); }); + +test('does not render a self-referential join link in the footer on the join screen', () => { + render( + + + } /> + + + ); + + const footerNav = screen.getByRole('navigation', { name: /footer/i }); + + expect(screen.getAllByRole('link', { name: /home/i })).toHaveLength(1); + expect(screen.getByRole('link', { name: /github/i })).toBeInTheDocument(); + expect(footerNav).not.toHaveTextContent(/join room/i); +}); diff --git a/src/pages/LandingPage.js b/src/pages/LandingPage.js index d3e7ddd..7985c1a 100644 --- a/src/pages/LandingPage.js +++ b/src/pages/LandingPage.js @@ -10,6 +10,7 @@ import { Sun, Moon, } from 'lucide-react'; +import SiteFooter from '../components/SiteFooter'; const LandingPage = () => { const navigate = useNavigate(); @@ -192,15 +193,9 @@ const LandingPage = () => {
- {/* Footer */} - + ); }; export default LandingPage; - - - diff --git a/src/pages/NotFound.js b/src/pages/NotFound.js index 900995b..7d807db 100644 --- a/src/pages/NotFound.js +++ b/src/pages/NotFound.js @@ -1,5 +1,6 @@ import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; +import SiteFooter from '../components/SiteFooter'; function NotFound() { const navigate = useNavigate(); @@ -14,46 +15,23 @@ function NotFound() { }, []); return ( -
-

404

-

- Page Not Found -

-

- Oops! The page you're looking for doesn't exist or has been moved. -

- +
+
+

404

+

Page Not Found

+

+ Oops! The page you're looking for doesn't exist or has been moved. +

+ +
+
); } -export default NotFound; \ No newline at end of file +export default NotFound;