diff --git a/README.md b/README.md index 8355f11..2a103a9 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,10 @@ Each user owns **projects**, and each project holds ordered **cards**. | 8 | Passwordless login — magic-link by default, password login behind a toggle | ✅ done | | 9 | Per-project card statuses ("To do" / "Doing" / "Done"); status chip, new cards start with none | ✅ done | | 10 | Project view — full-width, tabbed: alphabetical "All tasks" list + "Kanban" board, per-column drag ordering | ✅ done | -| 11 | Persistent left sidebar (Dashboard link + project list/new-project form); dashboard = grid of project tiles | ✅ done | +| 11 | Persistent left sidebar (Dashboard link + project dropdown); dashboard = grid of project tiles + new-project form | ✅ done | | 12 | Passwordless-only auth — registration and password login removed; a magic link is the sole way in, and creates the account if needed | ✅ done | | 13 | Global inbox — cards can have no project; moved into the sidebar, drag in/out of any project's kanban columns | ✅ done | +| 14 | New-project form moved to the dashboard; sidebar project list is now a switcher dropdown; Kanban is a project's default tab | ✅ done | There is no password. Signing in is entering an email address and opening the magic link sent to it — the same step creates the account the first time. See diff --git a/web/README.md b/web/README.md index 74ac82c..ec90221 100644 --- a/web/README.md +++ b/web/README.md @@ -37,7 +37,7 @@ src/stores/cards.ts Pinia store: one project's cards (CRUD; no reordering -- src/stores/inbox.ts Pinia store: the caller's global inbox (fetch + create) src/lib/api.ts fetch wrapper, bearer token, typed ApiError src/lib/cardOrder.ts reorderColumn() -- PUT /api/cards/order, shared by the sidebar and kanban board -src/components/AppSidebar.vue left nav: Dashboard link, project list + form, Inbox + form +src/components/AppSidebar.vue left nav: Dashboard link, project dropdown, Inbox + form src/components/CardRow.vue editable text + status chip + delete, one card src/components/KanbanCard.vue small draggable card for the board columns and the inbox src/views/ DashboardView, ProjectView, LoginView, ProfileView, @@ -47,14 +47,19 @@ src/views/ DashboardView, ProjectView, LoginView, ProfileView, Signed-in "app" routes (`meta.requiresAuth`) render inside a persistent shell: the top bar, then a left **sidebar** (`AppSidebar.vue`) beside the routed view. The sidebar stays mounted across navigation — it holds a **Dashboard** link, a -divider, the project list + new-project form, another divider, then the -**Inbox** (see below). The current page is highlighted via RouterLink's -`active-class`. `` remounts the view on every -path change so sidebar → project → project navigation always does a fresh load. +divider, a project ` - - {{ createError.fieldError('title') }} - - - {{ createError.message }} - - - Limit of {{ MAX_PROJECTS }} projects reached. - + diff --git a/web/src/style.css b/web/src/style.css index 4fc2b5f..92eafa9 100644 --- a/web/src/style.css +++ b/web/src/style.css @@ -158,9 +158,23 @@ body { color: var(--muted); } -.sidebar__projects { - max-height: 45vh; - overflow-y: auto; +.sidebar__select { + display: flex; + align-items: center; + gap: 0.6rem; + padding: 0.2rem 0.6rem; +} + +.sidebar__select select { + flex: 1; + min-width: 0; + font: inherit; + font-size: 0.9rem; + padding: 0.35rem 0.4rem; + border: 1px solid var(--border); + border-radius: 6px; + background: var(--bg); + color: var(--text); } .sidebar__inbox-cards { @@ -174,31 +188,6 @@ body { font-size: 0.85rem; } -.sidebar__form { - display: flex; - flex-direction: column; - gap: 0.4rem; - margin-top: 1rem; - padding-top: 1rem; - border-top: 1px solid var(--border); -} - -.sidebar__form input { - font: inherit; - font-size: 0.9rem; - padding: 0.4rem 0.55rem; - border: 1px solid var(--border); - border-radius: 6px; - background: var(--bg); - color: var(--text); -} - -.sidebar__form button[type='submit'] { - padding: 0.45rem 0.7rem; - font-size: 0.9rem; - border-radius: 6px; -} - .card { background: var(--surface); border: 1px solid var(--border); @@ -644,6 +633,7 @@ h1 { margin: 1.25rem 0; } +.form--new-project, .form--new-card { margin-top: 1.5rem; padding-top: 1.25rem; diff --git a/web/src/views/DashboardView.vue b/web/src/views/DashboardView.vue index 99537a9..f567531 100644 --- a/web/src/views/DashboardView.vue +++ b/web/src/views/DashboardView.vue @@ -1,13 +1,19 @@ diff --git a/web/src/views/ProjectView.vue b/web/src/views/ProjectView.vue index 71e3391..ba2fd65 100644 --- a/web/src/views/ProjectView.vue +++ b/web/src/views/ProjectView.vue @@ -23,11 +23,12 @@ const statuses = ref([]) const loadError = ref(null) const actionError = ref(null) +// Tab order is unchanged (All tasks first); Kanban is just the default view. const tabs = [ { key: 'all', label: 'All tasks' }, { key: 'kanban', label: 'Kanban' }, ] as const -const activeTab = ref<(typeof tabs)[number]['key']>('all') +const activeTab = ref<(typeof tabs)[number]['key']>('kanban') const titleDraft = ref('') const descriptionDraft = ref('')