Skip to content

Commit 4804e3c

Browse files
committed
update the UI
1 parent c3b6257 commit 4804e3c

7 files changed

Lines changed: 972 additions & 481 deletions

File tree

app/main.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,8 @@ async def lifespan(app: FastAPI):
5757

5858

5959
app = FastAPI(title="TinyURL", lifespan=lifespan)
60-
61-
6260
app.add_middleware(SessionMiddleware, secret_key="super-secret-key")
6361

64-
6562
BASE_DIR = Path(__file__).resolve().parent
6663
STATIC_DIR = BASE_DIR / "static"
6764

@@ -82,6 +79,7 @@ def build_short_url(short_code: str, request_host_url: str) -> str:
8279
async def index(request: Request):
8380
session = request.session
8481

82+
# Pop session variables
8583
new_short_url = session.pop("new_short_url", None)
8684
qr_enabled = session.pop("qr_enabled", False)
8785
qr_type = session.pop("qr_type", "short")
@@ -93,16 +91,25 @@ async def index(request: Request):
9391
qr_image = None
9492
qr_data = None
9593

94+
# --- RESTORED GENERATION LOGIC ---
9695
if qr_enabled and new_short_url and short_code:
9796
qr_data = new_short_url if qr_type == "short" else original_url
9897
qr_filename = f"{short_code}.png"
99-
generate_qr_with_logo(qr_data, qr_filename)
98+
99+
# Ensure the directory exists (Ubuntu best practice)
100+
qr_dir = STATIC_DIR / "qr"
101+
qr_dir.mkdir(parents=True, exist_ok=True)
102+
103+
# Generate the physical file
104+
generate_qr_with_logo(qr_data, str(qr_dir / qr_filename))
100105
qr_image = f"/static/qr/{qr_filename}"
106+
# --------------------------------
101107

108+
# Fetch URLs for the Bento Grid
102109
all_urls = []
103110
if db_available(request) and db_data.urls is not None:
104111
try:
105-
all_urls = list(db_data.urls.find().sort("created_at", -1))
112+
all_urls = list(db_data.urls.find().sort("created_at", -1).limit(10))
106113
except PyMongoError:
107114
all_urls = []
108115

@@ -112,12 +119,12 @@ async def index(request: Request):
112119
"request": request,
113120
"urls": all_urls,
114121
"new_short_url": new_short_url,
115-
"error": error,
116-
"info_message": info_message,
122+
"qr_image": qr_image,
117123
"qr_data": qr_data,
118124
"qr_enabled": qr_enabled,
119-
"qr_type": qr_type,
120-
"qr_image": qr_image,
125+
"original_url": original_url,
126+
"error": error,
127+
"info_message": info_message,
121128
"db_available": db_available(request),
122129
},
123130
)

app/static/css/tiny.css

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
:root {
2+
--bg: #0a0a0c;
3+
--glass: rgba(255, 255, 255, 0.03);
4+
--glass-border: rgba(255, 255, 255, 0.07);
5+
--accent: #6366f1;
6+
--text-primary: #ffffff;
7+
--text-secondary: #a1a1aa;
8+
}
9+
10+
body {
11+
background: var(--bg);
12+
color: var(--text-primary);
13+
font-family: 'Inter', system-ui, sans-serif;
14+
margin: 0;
15+
overflow-x: hidden;
16+
background-image: radial-gradient(circle at 50% -20%, #1e1e2e 0%, transparent 50%);
17+
}
18+
19+
.main-layout {
20+
max-width: 900px;
21+
margin: 0 auto;
22+
padding: 6rem 1.5rem;
23+
display: flex;
24+
flex-direction: column;
25+
align-items: center;
26+
gap: 2rem;
27+
}
28+
29+
/* --- LARGE HERO INPUT --- */
30+
.hero-input-card {
31+
width: 100%;
32+
background: var(--glass);
33+
backdrop-filter: blur(20px);
34+
border: 1px solid var(--glass-border);
35+
border-radius: 2.5rem;
36+
padding: 3rem;
37+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
38+
}
39+
40+
.hero-input-card h1 {
41+
font-size: 3.5rem;
42+
font-weight: 800;
43+
letter-spacing: -3px;
44+
margin: 0 0 1.5rem 0;
45+
text-align: center;
46+
background: linear-gradient(to right, #fff, #666);
47+
-webkit-background-clip: text;
48+
-webkit-text-fill-color: transparent;
49+
}
50+
51+
.input-wrapper {
52+
position: relative;
53+
display: flex;
54+
gap: 1rem;
55+
}
56+
57+
.input-wrapper input {
58+
flex: 1;
59+
background: rgba(255, 255, 255, 0.05);
60+
border: 1px solid var(--glass-border);
61+
border-radius: 1.5rem;
62+
padding: 1.5rem 2rem;
63+
color: white;
64+
font-size: 1.2rem;
65+
outline: none;
66+
transition: all 0.3s ease;
67+
}
68+
69+
.input-wrapper input:focus {
70+
border-color: var(--accent);
71+
background: rgba(255, 255, 255, 0.08);
72+
box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1);
73+
}
74+
75+
/* --- OUTPUT CARD (Immediate Result) --- */
76+
.result-card {
77+
width: 100%;
78+
background: linear-gradient(145deg, rgba(99, 102, 241, 0.1), rgba(0, 0, 0, 0));
79+
border: 1px solid rgba(99, 102, 241, 0.2);
80+
border-radius: 2rem;
81+
padding: 2rem;
82+
display: flex;
83+
align-items: center;
84+
justify-content: space-between;
85+
animation: slideUp 0.6s cubic-bezier(0.16, 1, 0.3, 1);
86+
}
87+
88+
/* --- SCROLLABLE RECENT TRAY --- */
89+
.recent-tray {
90+
width: 100%;
91+
margin-top: 4rem;
92+
}
93+
94+
.recent-header {
95+
display: flex;
96+
justify-content: space-between;
97+
margin-bottom: 1rem;
98+
padding: 0 1rem;
99+
}
100+
101+
.scroll-container {
102+
display: flex;
103+
gap: 1rem;
104+
overflow-x: auto;
105+
padding: 1rem;
106+
mask-image: linear-gradient(to right, black 85%, transparent);
107+
}
108+
109+
.scroll-container::-webkit-scrollbar {
110+
display: none;
111+
}
112+
113+
.recent-item {
114+
min-width: 250px;
115+
background: var(--glass);
116+
border: 1px solid var(--glass-border);
117+
padding: 1.5rem;
118+
border-radius: 1.5rem;
119+
font-size: 0.9rem;
120+
}
121+
122+
@keyframes slideUp {
123+
from {
124+
opacity: 0;
125+
transform: translateY(20px);
126+
}
127+
128+
to {
129+
opacity: 1;
130+
transform: translateY(0);
131+
}
132+
}
133+
134+
/* --- LARGE FOOTER --- */
135+
.big-footer {
136+
background: rgba(255, 255, 255, 0.01);
137+
border-top: 1px solid var(--border);
138+
padding: 6rem 0 3rem 0;
139+
margin-top: 8rem;
140+
}
141+
142+
.footer-grid {
143+
max-width: 1200px;
144+
margin: 0 auto;
145+
padding: 0 1.5rem;
146+
display: grid;
147+
grid-template-columns: 2fr 1fr 1fr 1fr;
148+
gap: 4rem;
149+
}
150+
151+
.footer-brand h3 {
152+
font-size: 1.5rem;
153+
margin: 0 0 1rem 0;
154+
letter-spacing: -1px;
155+
}
156+
157+
.footer-brand p {
158+
color: var(--text-dim);
159+
line-height: 1.6;
160+
max-width: 320px;
161+
}
162+
163+
.footer-col h4 {
164+
font-size: 0.85rem;
165+
text-transform: uppercase;
166+
letter-spacing: 0.1em;
167+
margin-bottom: 1.5rem;
168+
color: var(--text);
169+
}
170+
171+
.footer-col ul {
172+
list-style: none;
173+
padding: 0;
174+
margin: 0;
175+
}
176+
177+
.footer-col ul li {
178+
margin-bottom: 0.8rem;
179+
}
180+
181+
.footer-col ul li a {
182+
color: var(--text-dim);
183+
text-decoration: none;
184+
font-size: 0.9rem;
185+
transition: color 0.2s;
186+
}
187+
188+
.footer-col ul li a:hover {
189+
color: var(--accent);
190+
}
191+
192+
.footer-bottom {
193+
max-width: 1200px;
194+
margin: 4rem auto 0;
195+
padding: 2rem 1.5rem 0;
196+
border-top: 1px solid var(--border);
197+
display: flex;
198+
justify-content: space-between;
199+
align-items: center;
200+
color: var(--text-dim);
201+
font-size: 0.8rem;
202+
}
203+
204+
@media (max-width: 900px) {
205+
.footer-grid {
206+
grid-template-columns: 1fr 1fr;
207+
}
208+
}

app/static/qr/Q2RGq9.png

40 KB
Loading

0 commit comments

Comments
 (0)