refactor(web): remove old routes and BottomNav for single-screen redesign
Delete /tasks/new, /tasks/[uuid], /projects, /tags routes and BottomNav component. Simplify layout to slot-only with 100dvh flexbox. Remove nav-height CSS variable and .page padding rules. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+1
-13
@@ -40,7 +40,6 @@
|
|||||||
--font-size-2xl: 1.5rem;
|
--font-size-2xl: 1.5rem;
|
||||||
|
|
||||||
/* Layout */
|
/* Layout */
|
||||||
--nav-height: 60px;
|
|
||||||
--content-max-width: 768px;
|
--content-max-width: 768px;
|
||||||
|
|
||||||
/* Shadows */
|
/* Shadows */
|
||||||
@@ -67,7 +66,7 @@ html {
|
|||||||
body {
|
body {
|
||||||
background-color: var(--bg-secondary);
|
background-color: var(--bg-secondary);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
min-height: 100vh;
|
min-height: 100dvh;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,11 +112,6 @@ a:hover {
|
|||||||
padding: 0 var(--spacing-md);
|
padding: 0 var(--spacing-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page {
|
|
||||||
min-height: calc(100vh - var(--nav-height));
|
|
||||||
padding-bottom: calc(var(--nav-height) + var(--spacing-md));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Utility Classes */
|
/* Utility Classes */
|
||||||
.text-center {
|
.text-center {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -178,9 +172,3 @@ html {
|
|||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Safe Area Padding (for mobile notches) */
|
|
||||||
@supports (padding: env(safe-area-inset-bottom)) {
|
|
||||||
.page {
|
|
||||||
padding-bottom: calc(var(--nav-height) + var(--spacing-md) + env(safe-area-inset-bottom));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { page } from '$app/stores';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} path
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
function isActive(path) {
|
|
||||||
return $page.url.pathname === path || $page.url.pathname.startsWith(path + '/');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<nav class="bottom-nav">
|
|
||||||
<a href="/" class="nav-item" class:active={$page.url.pathname === '/'}>
|
|
||||||
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
|
|
||||||
</svg>
|
|
||||||
<span class="label">Tasks</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="/projects" class="nav-item" class:active={isActive('/projects')}>
|
|
||||||
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" />
|
|
||||||
</svg>
|
|
||||||
<span class="label">Projects</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="/tags" class="nav-item" class:active={isActive('/tags')}>
|
|
||||||
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
|
|
||||||
</svg>
|
|
||||||
<span class="label">Tags</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="/settings" class="nav-item" class:active={isActive('/settings')}>
|
|
||||||
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
||||||
</svg>
|
|
||||||
<span class="label">Settings</span>
|
|
||||||
</a>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.bottom-nav {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: var(--nav-height);
|
|
||||||
background-color: var(--bg-primary);
|
|
||||||
border-top: 1px solid var(--border-color);
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-around;
|
|
||||||
align-items: center;
|
|
||||||
z-index: 100;
|
|
||||||
padding-bottom: env(safe-area-inset-bottom, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 0.25rem;
|
|
||||||
flex: 1;
|
|
||||||
height: 100%;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
text-decoration: none;
|
|
||||||
transition: color 0.2s;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item.active {
|
|
||||||
color: var(--color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
width: 1.5rem;
|
|
||||||
height: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
font-size: var(--font-size-xs);
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,32 +1,16 @@
|
|||||||
<script>
|
<script>
|
||||||
import '../app.css';
|
import '../app.css';
|
||||||
import BottomNav from '$lib/components/BottomNav.svelte';
|
|
||||||
import { authStore } from '$lib/stores/auth.js';
|
|
||||||
import { page } from '$app/stores';
|
|
||||||
|
|
||||||
// Check if on auth pages (don't show nav)
|
|
||||||
$: isAuthPage = $page.url.pathname.startsWith('/auth');
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="app">
|
<div class="app">
|
||||||
<main class="main">
|
|
||||||
<slot />
|
<slot />
|
||||||
</main>
|
|
||||||
|
|
||||||
{#if $authStore.isAuthenticated && !isAuthPage}
|
|
||||||
<BottomNav />
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.app {
|
.app {
|
||||||
min-height: 100vh;
|
height: 100dvh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
overflow: hidden;
|
||||||
|
|
||||||
.main {
|
|
||||||
flex: 1;
|
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
<div class="page">
|
|
||||||
<div class="container">
|
|
||||||
<h1>Projects</h1>
|
|
||||||
<p class="text-secondary">Projects view coming soon...</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<div class="page">
|
|
||||||
<div class="container">
|
|
||||||
<h1>Tags</h1>
|
|
||||||
<p class="text-secondary">Tags view coming soon...</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { page } from '$app/stores';
|
|
||||||
import { goto } from '$app/navigation';
|
|
||||||
|
|
||||||
$: uuid = $page.params.uuid;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="page">
|
|
||||||
<div class="container">
|
|
||||||
<h1>Task Detail</h1>
|
|
||||||
<p class="text-secondary">UUID: {uuid}</p>
|
|
||||||
<p class="text-secondary mb-lg">Task detail view coming in next iteration...</p>
|
|
||||||
<a href="/" class="btn-link">← Back to tasks</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.btn-link {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0.75rem 1.5rem;
|
|
||||||
background-color: var(--color-primary);
|
|
||||||
color: white;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { goto } from '$app/navigation';
|
|
||||||
import { tasksStore } from '$lib/stores/tasks.js';
|
|
||||||
import { generateUUID } from '$lib/utils/uuid.js';
|
|
||||||
import { toUnix } from '$lib/utils/dates.js';
|
|
||||||
import Button from '$lib/components/ui/Button.svelte';
|
|
||||||
import Input from '$lib/components/ui/Input.svelte';
|
|
||||||
import Select from '$lib/components/ui/Select.svelte';
|
|
||||||
|
|
||||||
let description = '';
|
|
||||||
let project = '';
|
|
||||||
let priority = '1';
|
|
||||||
let dueDate = '';
|
|
||||||
let tags = '';
|
|
||||||
let saving = false;
|
|
||||||
let error = '';
|
|
||||||
|
|
||||||
const priorityOptions = [
|
|
||||||
{ value: '0', label: 'Low' },
|
|
||||||
{ value: '1', label: 'Default' },
|
|
||||||
{ value: '2', label: 'Medium' },
|
|
||||||
{ value: '3', label: 'High' }
|
|
||||||
];
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
if (!description.trim()) {
|
|
||||||
error = 'Description is required';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
saving = true;
|
|
||||||
error = '';
|
|
||||||
|
|
||||||
try {
|
|
||||||
/** @type {Partial<import('$lib/api/types.js').Task>} */
|
|
||||||
const task = {
|
|
||||||
uuid: generateUUID(),
|
|
||||||
description: description.trim(),
|
|
||||||
status: /** @type {'P'} */ ('P'),
|
|
||||||
priority: /** @type {0|1|2|3} */ (parseInt(priority)),
|
|
||||||
created: toUnix(new Date()),
|
|
||||||
modified: toUnix(new Date()),
|
|
||||||
tags: tags.trim() ? tags.split(',').map(t => t.trim()).filter(t => t) : [],
|
|
||||||
project: project.trim() || null,
|
|
||||||
due: dueDate ? toUnix(new Date(dueDate)) : null
|
|
||||||
};
|
|
||||||
|
|
||||||
await tasksStore.add(task);
|
|
||||||
goto('/');
|
|
||||||
} catch (err) {
|
|
||||||
error = err instanceof Error ? err.message : 'Failed to create task';
|
|
||||||
} finally {
|
|
||||||
saving = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="page">
|
|
||||||
<div class="container">
|
|
||||||
<div class="header">
|
|
||||||
<a href="/" class="back-btn">
|
|
||||||
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
|
||||||
</svg>
|
|
||||||
Back
|
|
||||||
</a>
|
|
||||||
<h1>New Task</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form on:submit|preventDefault={handleSubmit} class="task-form">
|
|
||||||
{#if error}
|
|
||||||
<div class="error-banner">{error}</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<Input
|
|
||||||
label="Description"
|
|
||||||
placeholder="What needs to be done?"
|
|
||||||
bind:value={description}
|
|
||||||
required
|
|
||||||
id="description"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
label="Project"
|
|
||||||
placeholder="Optional"
|
|
||||||
bind:value={project}
|
|
||||||
id="project"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Select
|
|
||||||
label="Priority"
|
|
||||||
options={priorityOptions}
|
|
||||||
bind:value={priority}
|
|
||||||
id="priority"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
label="Due Date"
|
|
||||||
type="date"
|
|
||||||
bind:value={dueDate}
|
|
||||||
id="due"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
label="Tags"
|
|
||||||
placeholder="Comma separated (e.g., work, urgent)"
|
|
||||||
bind:value={tags}
|
|
||||||
id="tags"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="actions">
|
|
||||||
<Button type="button" variant="secondary" on:click={() => goto('/')}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button type="submit" loading={saving}>
|
|
||||||
Create Task
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--spacing-md);
|
|
||||||
margin-bottom: var(--spacing-lg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-btn {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.25rem;
|
|
||||||
color: var(--color-primary);
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: var(--font-size-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
width: 1.25rem;
|
|
||||||
height: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.task-form {
|
|
||||||
background-color: var(--bg-primary);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
padding: var(--spacing-lg);
|
|
||||||
box-shadow: var(--shadow-sm);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--spacing-md);
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-banner {
|
|
||||||
background-color: #fee2e2;
|
|
||||||
color: var(--color-danger);
|
|
||||||
padding: var(--spacing-md);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
font-size: var(--font-size-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--spacing-md);
|
|
||||||
margin-top: var(--spacing-md);
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions > :global(button) {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
Reference in New Issue
Block a user