c5b049edba
Self-hosted personal task list: FastAPI + SQLite in Docker, mobile-friendly PWA frontend, deadlines, priorities, categories, tags, notes, checklists, recurring tasks, single-password login, and JSON export/import. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
173 lines
6.7 KiB
HTML
173 lines
6.7 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||
<meta name="theme-color" content="#4f46e5">
|
||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-title" content="WhatToDo">
|
||
<title>WhatToDo</title>
|
||
<link rel="manifest" href="manifest.webmanifest">
|
||
<link rel="icon" href="icons/icon.svg" type="image/svg+xml">
|
||
<link rel="apple-touch-icon" href="icons/icon-180.png">
|
||
<link rel="stylesheet" href="style.css">
|
||
<script src="app.js" defer></script>
|
||
</head>
|
||
<body>
|
||
<div id="login" class="login" hidden>
|
||
<form id="login-form" class="login-card">
|
||
<img src="icons/icon.svg" alt="" width="56" height="56">
|
||
<h1>WhatToDo</h1>
|
||
<input type="password" id="login-password" placeholder="Password" autocomplete="current-password" required>
|
||
<button type="submit" class="btn primary">Log in</button>
|
||
<p id="login-error" class="error" hidden></p>
|
||
</form>
|
||
</div>
|
||
|
||
<div id="app" class="app" hidden>
|
||
<aside id="sidebar" class="sidebar">
|
||
<div class="brand"><img src="icons/icon.svg" alt="" width="28" height="28"> WhatToDo</div>
|
||
<nav id="views" class="nav"></nav>
|
||
<div class="nav-heading">Categories</div>
|
||
<nav id="cats" class="nav"></nav>
|
||
<div class="sidebar-foot">
|
||
<button id="settings-btn" class="nav-item" type="button"><span class="nav-icon">⚙️</span> Settings</button>
|
||
</div>
|
||
</aside>
|
||
<div id="scrim" class="scrim"></div>
|
||
|
||
<main class="main">
|
||
<header class="topbar">
|
||
<button id="menu-btn" class="icon-btn menu-btn" type="button" aria-label="Open menu">☰</button>
|
||
<h2 id="view-title"></h2>
|
||
<input id="search" type="search" placeholder="Search or #tag" autocomplete="off">
|
||
</header>
|
||
<form id="quick-add" class="quick-add">
|
||
<input id="quick-title" placeholder="Add a task…" autocomplete="off" maxlength="500">
|
||
<button class="btn primary" type="submit">Add</button>
|
||
<button id="new-detailed" class="btn" type="button" title="New task with details">+ Details</button>
|
||
</form>
|
||
<div id="list" class="list"></div>
|
||
</main>
|
||
</div>
|
||
|
||
<dialog id="task-dialog">
|
||
<form id="task-form" class="dialog-body">
|
||
<h3 id="task-dialog-title">Task</h3>
|
||
<label class="field">
|
||
<span>Title</span>
|
||
<input id="t-title" required maxlength="500" autocomplete="off">
|
||
</label>
|
||
<div class="row">
|
||
<label class="field">
|
||
<span>Category</span>
|
||
<select id="t-category"></select>
|
||
</label>
|
||
<label class="field">
|
||
<span>Priority</span>
|
||
<select id="t-priority">
|
||
<option value="0">None</option>
|
||
<option value="1">Low</option>
|
||
<option value="2">Medium</option>
|
||
<option value="3">High</option>
|
||
</select>
|
||
</label>
|
||
</div>
|
||
<div class="row">
|
||
<label class="field">
|
||
<span>Deadline</span>
|
||
<input id="t-due" type="date">
|
||
</label>
|
||
<label class="field">
|
||
<span>Repeat</span>
|
||
<select id="t-repeat">
|
||
<option value="none">Never</option>
|
||
<option value="daily">Daily</option>
|
||
<option value="weekly">Weekly</option>
|
||
<option value="monthly">Monthly</option>
|
||
<option value="yearly">Yearly</option>
|
||
</select>
|
||
</label>
|
||
<label class="field narrow" id="t-interval-wrap">
|
||
<span>Every</span>
|
||
<input id="t-interval" type="number" min="1" max="365" value="1">
|
||
</label>
|
||
</div>
|
||
<div class="quick-dates">
|
||
<button type="button" class="chip-btn" data-days="0">Today</button>
|
||
<button type="button" class="chip-btn" data-days="1">Tomorrow</button>
|
||
<button type="button" class="chip-btn" data-days="7">Next week</button>
|
||
<button type="button" class="chip-btn" data-days="">No date</button>
|
||
</div>
|
||
<label class="field">
|
||
<span>Tags <small>(comma separated)</small></span>
|
||
<input id="t-tags" placeholder="e.g. editing, urgent" autocomplete="off">
|
||
</label>
|
||
<label class="field">
|
||
<span>Notes</span>
|
||
<textarea id="t-notes" rows="4" maxlength="20000"></textarea>
|
||
</label>
|
||
<div class="field">
|
||
<span>Checklist</span>
|
||
<ul id="t-subtasks" class="subtasks"></ul>
|
||
<div class="subtask-add">
|
||
<input id="t-subtask-new" placeholder="Add a step and press Enter" autocomplete="off" maxlength="500">
|
||
<button id="t-subtask-add" type="button" class="btn">Add</button>
|
||
</div>
|
||
</div>
|
||
<div class="dialog-actions">
|
||
<button id="t-delete" type="button" class="btn danger">Delete</button>
|
||
<span class="spacer"></span>
|
||
<button id="t-cancel" type="button" class="btn">Cancel</button>
|
||
<button type="submit" class="btn primary">Save</button>
|
||
</div>
|
||
</form>
|
||
</dialog>
|
||
|
||
<dialog id="settings-dialog">
|
||
<div class="dialog-body">
|
||
<h3>Settings</h3>
|
||
|
||
<section class="settings-section">
|
||
<h4>Categories</h4>
|
||
<ul id="s-cats" class="cat-edit-list"></ul>
|
||
<form id="s-cat-add" class="cat-add">
|
||
<input id="s-cat-color" type="color" value="#0ea5e9" aria-label="Color">
|
||
<input id="s-cat-name" placeholder="New category" maxlength="50" required autocomplete="off">
|
||
<button class="btn" type="submit">Add</button>
|
||
</form>
|
||
</section>
|
||
|
||
<section class="settings-section">
|
||
<h4>Export / import</h4>
|
||
<p class="muted">Export downloads all tasks and categories as a JSON file. Import it on another
|
||
install, such as production, to move your data there.</p>
|
||
<div class="row wrap">
|
||
<a id="s-export" class="btn primary" href="api/export" download>Export data</a>
|
||
</div>
|
||
<div class="row wrap import-row">
|
||
<input id="s-import-file" type="file" accept="application/json,.json">
|
||
<select id="s-import-mode" aria-label="Import mode">
|
||
<option value="merge">Merge (add to current data)</option>
|
||
<option value="replace">Replace (delete current data first)</option>
|
||
</select>
|
||
<button id="s-import-btn" class="btn" type="button">Import</button>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="settings-section">
|
||
<h4>Session</h4>
|
||
<button id="s-logout" class="btn" type="button">Log out</button>
|
||
</section>
|
||
|
||
<div class="dialog-actions">
|
||
<span class="spacer"></span>
|
||
<button id="s-close" class="btn primary" type="button">Done</button>
|
||
</div>
|
||
</div>
|
||
</dialog>
|
||
|
||
<div id="toast" class="toast" hidden></div>
|
||
</body>
|
||
</html>
|