Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 117 additions & 5 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand All @@ -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;
}
Expand Down
7 changes: 4 additions & 3 deletions src/App.test.js
Original file line number Diff line number Diff line change
@@ -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(<App />);
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();
});
33 changes: 33 additions & 0 deletions src/components/SiteFooter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Link } from 'react-router-dom';

function SiteFooter({ showJoinLink = true }) {
return (
<footer className="site-footer">
<div className="site-footer-inner">
<div className="site-footer-brand">
<div className="logo-text">
<span className="logo-collab">Collab</span>
<span className="logo-ce">CE</span>
</div>
<p className="site-footer-copy">
Real-time rooms, shared files, and collaboration without friction.
</p>
</div>

<nav className="site-footer-nav" aria-label="Footer">
<Link to="/">Home</Link>
{showJoinLink ? <Link to="/join">Join Room</Link> : null}
<a
href="https://github.com/suresh1319/Collab-Code-Editor"
target="_blank"
rel="noreferrer"
>
GitHub
</a>
</nav>
</div>
</footer>
);
}

export default SiteFooter;
100 changes: 52 additions & 48 deletions src/pages/Home.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -41,62 +42,65 @@ const Home = () => {
userName,
}
});
}
};
const handleInputEnter=(e)=>{
if(e.code==='Enter'){
joinRoom();
}
}
};
return (
<div className="homePageWrapper">
<button className="theme-toggle" onClick={toggleTheme} title="Toggle Theme">
{theme === 'light' ? '🌙' : '☀️'}
</button>
<div className="formWrapper">
<button
type="button"
className="backHomeBtn"
onClick={() => navigate('/')}
>
← Back to Home
<div className="homePageShell">
<div className="homePageWrapper">
<button className="theme-toggle" onClick={toggleTheme} title="Toggle Theme">
{theme === 'light' ? '🌙' : '☀️'}
</button>
<div className="home-logo-text">
<span className="logo-collab">Collab</span><span className="logo-ce">CE</span>
</div>
<h4 className="mainlabel">
{searchParams.get('roomId') ? '🎉 You were invited! Enter your username to join' : 'Paste Invitation Room Id'}
</h4>
<div className="inputGroup">
<input
type="text"
className="inputBox"
value={roomID}
onChange={(e) => setRoomId(e.target.value)}
placeholder="ROOM ID"
onKeyUp={handleInputEnter}
/>
<input
type="text"
value={userName}
onChange={(e) => setUserName(e.target.value)}
className="inputBox"
placeholder="USERNAME"
onKeyUp={handleInputEnter}
/>
<button onClick={joinRoom} className="btn joinbtn">Join</button>
<span className="createInfo">
Want to create a new room? Click here{" "}
<a
href="#"
onClick={createNewRoom}
id="create-room"
className="createNewBtn"
>
New Room
</a>
</span>
<div className="formWrapper">
<button
type="button"
className="backHomeBtn"
onClick={() => navigate('/')}
>
← Back to Home
</button>
<div className="home-logo-text">
<span className="logo-collab">Collab</span><span className="logo-ce">CE</span>
</div>
<h4 className="mainlabel">
{searchParams.get('roomId') ? '🎉 You were invited! Enter your username to join' : 'Paste Invitation Room Id'}
</h4>
<div className="inputGroup">
<input
type="text"
className="inputBox"
value={roomID}
onChange={(e) => setRoomId(e.target.value)}
placeholder="ROOM ID"
onKeyUp={handleInputEnter}
/>
<input
type="text"
value={userName}
onChange={(e) => setUserName(e.target.value)}
className="inputBox"
placeholder="USERNAME"
onKeyUp={handleInputEnter}
/>
<button onClick={joinRoom} className="btn joinbtn">Join</button>
<span className="createInfo">
Want to create a new room? Click here{" "}
<a
href="#"
onClick={createNewRoom}
id="create-room"
className="createNewBtn"
>
New Room
</a>
</span>
</div>
</div>
</div>
<SiteFooter showJoinLink={false} />
</div>
);
};
Expand Down
19 changes: 19 additions & 0 deletions src/pages/Home.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<MemoryRouter
initialEntries={['/join']}
future={{ v7_startTransition: true, v7_relativeSplatPath: true }}
>
<Routes>
<Route path="/join" element={<Home />} />
</Routes>
</MemoryRouter>
);

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);
});
9 changes: 2 additions & 7 deletions src/pages/LandingPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Sun,
Moon,
} from 'lucide-react';
import SiteFooter from '../components/SiteFooter';

const LandingPage = () => {
const navigate = useNavigate();
Expand Down Expand Up @@ -192,15 +193,9 @@ const LandingPage = () => {
</div>
</section>

{/* Footer */}
<footer className="landing-footer">
<p>Built with ❤️ for collaborative developers.</p>
</footer>
<SiteFooter />
</div>
);
};

export default LandingPage;



Loading