206 lines
7.2 KiB
HTML
206 lines
7.2 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
<title>K8s Todo App</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=DM+Sans:wght@300;400;600;700&display=swap" rel="stylesheet"/>
|
|
<style>
|
|
:root {
|
|
--bg: #0f0f13;
|
|
--surface: #1a1a24;
|
|
--surface2: #22222e;
|
|
--border: #2e2e3e;
|
|
--accent: #7c6af7;
|
|
--accent2: #4fd1c5;
|
|
--text: #e8e8f0;
|
|
--muted: #6b6b82;
|
|
--done-color: #4ade80;
|
|
--danger: #f87171;
|
|
}
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
body {
|
|
font-family: 'DM Sans', sans-serif;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
min-height: 100vh;
|
|
padding: 2rem 1rem;
|
|
}
|
|
.noise {
|
|
position: fixed; inset: 0; pointer-events: none; z-index: 0;
|
|
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.04'/%3E%3C/svg%3E");
|
|
opacity: 0.4;
|
|
}
|
|
.glow {
|
|
position: fixed; top: -200px; left: 50%; transform: translateX(-50%);
|
|
width: 600px; height: 400px; pointer-events: none; z-index: 0;
|
|
background: radial-gradient(ellipse at center, rgba(124,106,247,0.15) 0%, transparent 70%);
|
|
}
|
|
.container {
|
|
position: relative; z-index: 1;
|
|
max-width: 680px; margin: 0 auto;
|
|
}
|
|
header {
|
|
display: flex; align-items: center; justify-content: space-between;
|
|
margin-bottom: 2.5rem;
|
|
}
|
|
.logo {
|
|
display: flex; align-items: center; gap: 0.75rem;
|
|
}
|
|
.k8s-badge {
|
|
background: linear-gradient(135deg, #326de6, #7c6af7);
|
|
border-radius: 8px; padding: 6px 10px;
|
|
font-family: 'DM Mono', monospace; font-size: 0.7rem;
|
|
font-weight: 500; letter-spacing: 0.05em;
|
|
color: #fff;
|
|
}
|
|
h1 {
|
|
font-size: 1.5rem; font-weight: 700;
|
|
background: linear-gradient(135deg, var(--text), var(--muted));
|
|
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
|
}
|
|
.stats {
|
|
display: flex; gap: 1rem;
|
|
}
|
|
.stat {
|
|
background: var(--surface); border: 1px solid var(--border);
|
|
border-radius: 10px; padding: 0.5rem 1rem;
|
|
font-family: 'DM Mono', monospace; font-size: 0.8rem;
|
|
color: var(--muted);
|
|
}
|
|
.stat span { color: var(--accent); font-weight: 600; }
|
|
|
|
.add-form {
|
|
background: var(--surface); border: 1px solid var(--border);
|
|
border-radius: 16px; padding: 1.25rem;
|
|
display: flex; gap: 0.75rem;
|
|
margin-bottom: 1.5rem;
|
|
transition: border-color 0.2s;
|
|
}
|
|
.add-form:focus-within { border-color: var(--accent); }
|
|
.add-form input {
|
|
flex: 1; background: var(--surface2); border: 1px solid var(--border);
|
|
border-radius: 10px; padding: 0.75rem 1rem;
|
|
color: var(--text); font-family: 'DM Sans', sans-serif;
|
|
font-size: 0.95rem; outline: none;
|
|
transition: border-color 0.2s;
|
|
}
|
|
.add-form input:focus { border-color: var(--accent); }
|
|
.add-form input::placeholder { color: var(--muted); }
|
|
.btn {
|
|
background: linear-gradient(135deg, var(--accent), #9b8af8);
|
|
color: #fff; border: none; border-radius: 10px;
|
|
padding: 0.75rem 1.5rem; font-size: 0.9rem; font-weight: 600;
|
|
cursor: pointer; transition: opacity 0.2s, transform 0.1s;
|
|
white-space: nowrap;
|
|
}
|
|
.btn:hover { opacity: 0.9; }
|
|
.btn:active { transform: scale(0.97); }
|
|
|
|
.todo-list { display: flex; flex-direction: column; gap: 0.6rem; }
|
|
.todo-item {
|
|
background: var(--surface); border: 1px solid var(--border);
|
|
border-radius: 14px; padding: 1rem 1.25rem;
|
|
display: flex; align-items: center; gap: 1rem;
|
|
transition: border-color 0.2s, background 0.2s;
|
|
animation: slideIn 0.25s ease;
|
|
}
|
|
@keyframes slideIn {
|
|
from { opacity: 0; transform: translateY(-6px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
.todo-item:hover { border-color: var(--accent); background: var(--surface2); }
|
|
.todo-item.done { opacity: 0.6; }
|
|
.toggle-btn {
|
|
width: 22px; height: 22px; border-radius: 50%;
|
|
border: 2px solid var(--border); background: transparent;
|
|
cursor: pointer; flex-shrink: 0;
|
|
display: flex; align-items: center; justify-content: center;
|
|
transition: all 0.2s;
|
|
}
|
|
.toggle-btn:hover { border-color: var(--accent2); }
|
|
.done .toggle-btn {
|
|
background: var(--done-color); border-color: var(--done-color);
|
|
}
|
|
.done .toggle-btn::after {
|
|
content: '✓'; color: #000; font-size: 0.7rem; font-weight: 700;
|
|
}
|
|
.todo-title {
|
|
flex: 1; font-size: 0.95rem; line-height: 1.4;
|
|
transition: color 0.2s;
|
|
}
|
|
.done .todo-title { text-decoration: line-through; color: var(--muted); }
|
|
.todo-meta {
|
|
font-family: 'DM Mono', monospace; font-size: 0.7rem;
|
|
color: var(--muted); white-space: nowrap;
|
|
}
|
|
.delete-btn {
|
|
background: transparent; border: none; color: var(--muted);
|
|
cursor: pointer; font-size: 1rem; padding: 0.25rem;
|
|
border-radius: 6px; transition: color 0.2s, background 0.2s;
|
|
line-height: 1;
|
|
}
|
|
.delete-btn:hover { color: var(--danger); background: rgba(248,113,113,0.1); }
|
|
|
|
.empty {
|
|
text-align: center; padding: 4rem 2rem;
|
|
color: var(--muted); font-size: 0.95rem;
|
|
}
|
|
.empty .icon { font-size: 3rem; margin-bottom: 1rem; }
|
|
|
|
footer {
|
|
margin-top: 3rem; text-align: center;
|
|
font-family: 'DM Mono', monospace; font-size: 0.7rem;
|
|
color: var(--muted);
|
|
}
|
|
footer a { color: var(--accent); text-decoration: none; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="noise"></div>
|
|
<div class="glow"></div>
|
|
<div class="container">
|
|
<header>
|
|
<div class="logo">
|
|
<div class="k8s-badge">⎈ K8S</div>
|
|
<h1>Todo App</h1>
|
|
</div>
|
|
<div class="stats">
|
|
<div class="stat"><span>{{ done_count }}</span>/{{ total }} done</div>
|
|
</div>
|
|
</header>
|
|
|
|
<form class="add-form" action="/add" method="post">
|
|
<input type="text" name="title" placeholder="Add a new task..." autocomplete="off" required />
|
|
<button type="submit" class="btn">+ Add</button>
|
|
</form>
|
|
|
|
<div class="todo-list">
|
|
{% if todos %}
|
|
{% for todo in todos %}
|
|
<div class="todo-item {% if todo[2] %}done{% endif %}">
|
|
<a href="/toggle/{{ todo[0] }}" style="text-decoration:none">
|
|
<div class="toggle-btn"></div>
|
|
</a>
|
|
<span class="todo-title">{{ todo[1] }}</span>
|
|
<span class="todo-meta">{{ todo[3].strftime('%b %d') if todo[3] else '' }}</span>
|
|
<a href="/delete/{{ todo[0] }}">
|
|
<button class="delete-btn" title="Delete">✕</button>
|
|
</a>
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<div class="empty">
|
|
<div class="icon">📋</div>
|
|
<p>No tasks yet. Add one above!</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<footer>
|
|
running on <a href="#">kubernetes</a> · flask + postgresql
|
|
</footer>
|
|
</div>
|
|
</body>
|
|
</html>
|