Skip to content
Merged
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
3 changes: 1 addition & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
python -m pip install -r requirements.txt -r requirements-dev.txt
- name: Run tests
env:
DATABASE_URL: sqlite+pysqlite:///:memory:
DATABASE_URL: "sqlite+pysqlite:///:memory:"
ENABLE_GEOCODING: "0"
run: python -m pytest -q

Expand All @@ -49,4 +49,3 @@ jobs:
run: npm ci
- name: Build
run: npm run build

169 changes: 167 additions & 2 deletions frontend/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,9 @@
min-height: 100vh;
display: flex;
flex-direction: column;
background: radial-gradient(900px 520px at 16% 0%, rgba(37, 99, 235, 0.14), transparent 60%),
radial-gradient(900px 520px at 86% 0%, rgba(239, 68, 68, 0.12), transparent 60%),
#f8fafc;
}

.landingHeader {
Expand All @@ -273,20 +276,78 @@
justify-content: space-between;
padding: 14px 16px;
border-bottom: 1px solid #e2e8f0;
background: #ffffff;
background: rgba(255, 255, 255, 0.75);
backdrop-filter: blur(10px);
position: sticky;
top: 0;
z-index: 10;
}

.landingBrand {
font-weight: 800;
letter-spacing: 0.2px;
color: #0f172a;
display: flex;
align-items: center;
gap: 10px;
}

.landingBrandTag {
font-size: 11px;
font-weight: 700;
padding: 4px 8px;
border-radius: 999px;
color: #334155;
background: rgba(226, 232, 240, 0.7);
border: 1px solid rgba(226, 232, 240, 0.9);
}

.landingMain {
flex: 1;
width: min(1040px, 100%);
margin: 0 auto;
padding: 24px 16px 40px 16px;
}

.landingHero {
border: 1px solid rgba(226, 232, 240, 0.9);
border-radius: 18px;
padding: 18px 18px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.85), rgba(255, 255, 255, 0.72));
box-shadow: 0 18px 40px rgba(15, 23, 42, 0.07);
}

.landingHeroGrid {
display: grid;
grid-template-columns: 1.25fr 0.75fr;
gap: 18px;
align-items: center;
}

.landingHeroLeft {
min-width: 0;
}

.landingPills {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 10px;
}

.pill {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
border-radius: 999px;
font-size: 12px;
font-weight: 650;
color: #0f172a;
border: 1px solid rgba(226, 232, 240, 0.9);
background: rgba(248, 250, 252, 0.85);
}

.landingHero h1 {
margin: 0;
font-size: 34px;
Expand Down Expand Up @@ -316,6 +377,89 @@
color: #475569;
}

.landingHeroRight {
min-width: 0;
}

.landingMock {
border: 1px solid rgba(226, 232, 240, 0.9);
border-radius: 16px;
overflow: hidden;
background: rgba(255, 255, 255, 0.88);
box-shadow: 0 10px 26px rgba(15, 23, 42, 0.1);
}

.landingMockMap {
height: 170px;
position: relative;
background: radial-gradient(260px 150px at 30% 30%, rgba(37, 99, 235, 0.22), transparent 70%),
radial-gradient(240px 150px at 70% 70%, rgba(239, 68, 68, 0.18), transparent 70%),
linear-gradient(180deg, rgba(241, 245, 249, 0.9), rgba(226, 232, 240, 0.65));
}

.landingMockPin {
position: absolute;
width: 12px;
height: 12px;
border-radius: 999px;
border: 2px solid rgba(255, 255, 255, 0.95);
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.22);
transform: translate(-50%, -50%);
}

.landingMockPin.listing {
background: #2563eb;
}

.landingMockPin.target {
width: 14px;
height: 14px;
background: #ef4444;
box-shadow: 0 0 0 4px rgba(239, 68, 68, 0.22), 0 8px 20px rgba(15, 23, 42, 0.22);
}

.landingMockList {
padding: 12px 12px 12px 12px;
display: flex;
flex-direction: column;
gap: 10px;
}

.landingMockItem {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 10px;
border-radius: 12px;
border: 1px solid rgba(226, 232, 240, 0.9);
background: rgba(248, 250, 252, 0.88);
font-size: 12px;
color: #0f172a;
}

.landingMockDot {
width: 10px;
height: 10px;
border-radius: 999px;
flex: 0 0 auto;
}

.landingMockDot.listing {
background: #2563eb;
}

.landingMockText {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

.landingMockMeta {
margin-left: auto;
color: #475569;
font-variant-numeric: tabular-nums;
}

.landingGrid {
margin-top: 24px;
display: grid;
Expand Down Expand Up @@ -352,10 +496,27 @@

.landingFooter {
border-top: 1px solid #e2e8f0;
background: #ffffff;
background: rgba(255, 255, 255, 0.75);
backdrop-filter: blur(10px);
padding: 14px 16px;
font-size: 12px;
color: #475569;
display: flex;
gap: 8px;
flex-wrap: wrap;
align-items: center;
justify-content: center;
text-align: center;
}

.landingFooter a {
color: inherit;
text-decoration: underline;
text-underline-offset: 2px;
}

.landingFooter a:hover {
color: #0f172a;
}

@media (max-width: 960px) {
Expand All @@ -373,6 +534,10 @@
grid-template-columns: 1fr;
}

.landingHeroGrid {
grid-template-columns: 1fr;
}

.landingHero h1 {
font-size: 28px;
}
Expand Down
97 changes: 77 additions & 20 deletions frontend/src/pages/LandingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,75 @@ export default function LandingPage() {
return (
<div className="landing">
<header className="landingHeader">
<div className="landingBrand">EasyRelocate</div>
<div className="actions">
<Link className="button" to="/compare">
Open app
</Link>
<div className="landingBrand">
EasyRelocate <span className="landingBrandTag">US-only MVP</span>
</div>
</header>

<main className="landingMain">
<section className="landingHero">
<h1>Relocation housing, compared in one place.</h1>
<p>
EasyRelocate is an open-source, non-commercial decision-support tool
for interns/students relocating to a new city. Save listings while
you browse (starting with Airbnb), then compare them on a single map
with price + distance filters.
</p>
<div className="landingCtas">
<Link className="button" to="/compare">
Start comparing
</Link>
</div>
<div className="landingNote">
MVP is US-only for now. Listing locations may be approximate.
<div className="landingHeroGrid">
<div className="landingHeroLeft">
<div className="landingPills" aria-label="Highlights">
<span className="pill">Chrome extension</span>
<span className="pill">Map + list</span>
<span className="pill">Open-source</span>
</div>
<h1>Relocation housing, compared in one place.</h1>
<p>
EasyRelocate is a non-commercial decision-support tool for interns/students
relocating to a new city. Save listings while you browse (starting with
Airbnb), then compare them on one map with price + distance filters.
</p>
<div className="landingCtas">
<Link className="button" to="/compare">
Start comparing
</Link>
<a
className="button secondary"
href="https://github.com/YuWei-CH/EasyRelocate/blob/main/docs/PLATFORM_ORGANIZATION.md"
target="_blank"
rel="noreferrer"
>
How it works
</a>
</div>
<div className="landingNote">
US-only MVP for now. Listing locations may be approximate.
</div>
</div>

<div className="landingHeroRight" aria-hidden="true">
<div className="landingMock">
<div className="landingMockMap">
<div className="landingMockPin target" style={{ left: '56%', top: '42%' }} />
<div className="landingMockPin listing" style={{ left: '34%', top: '34%' }} />
<div className="landingMockPin listing" style={{ left: '70%', top: '62%' }} />
<div className="landingMockPin listing" style={{ left: '46%', top: '68%' }} />
</div>
<div className="landingMockList">
<div className="landingMockItem">
<span className="landingMockDot listing" />
<span className="landingMockText">Airbnb · 1BR · $3,200/mo</span>
<span className="landingMockMeta">3.4 km</span>
</div>
<div className="landingMockItem">
<span className="landingMockDot listing" />
<span className="landingMockText">Airbnb · Studio · $2,750/mo</span>
<span className="landingMockMeta">5.1 km</span>
</div>
<div className="landingMockItem">
<span className="landingMockDot listing" />
<span className="landingMockText">Airbnb · 2BR · $3,900/mo</span>
<span className="landingMockMeta">7.8 km</span>
</div>
</div>
</div>
</div>
</div>
</section>

<section className="landingGrid">
<section className="landingGrid" id="how">
<div className="landingCard">
<h2>What &amp; why</h2>
<ul>
Expand Down Expand Up @@ -73,6 +114,22 @@ export default function LandingPage() {

<footer className="landingFooter">
<div>EasyRelocate — open-source, non-commercial.</div>
<div>
All copyright reserved by{' '}
<a href="https://github.com/YuWei-CH" target="_blank" rel="noreferrer">
YuWei-CH
</a>
</div>
<div>
Welcome to contribute:{' '}
<a
href="https://github.com/YuWei-CH/EasyRelocate"
target="_blank"
rel="noreferrer"
>
https://github.com/YuWei-CH/EasyRelocate
</a>
</div>
</footer>
</div>
)
Expand Down