feat: kanban status buttons, task back-nav, RSS UI, weather search, briefing fixes
Project view: - Add inline status advance buttons on kanban task cards (todo→in_progress, in_progress→done); buttons reveal on hover, stop link navigation Task viewer: - Back button navigates to task's project instead of /tasks when project_id set - Esc key navigates to project (or /tasks); blurs focused element first Quick capture: - Use user's configured model instead of hardcoded Config.OLLAMA_MODEL - Remove create_project from classifier prompt (tool not offered, caused task-shaped inputs to silently fall through to note fallback) Briefing scheduler: - Fix get_event_loop() → get_running_loop() so background thread uses the correct hypercorn event loop (jobs were scheduling but never executing) - Suppress bare greeting when both LLM synthesis lanes return empty RSS feed UI (SettingsView): - Show last-fetched age, category badge, and feed URL per row - Category input field when adding a feed - Refresh all button: fetches latest items, reloads list, toasts with count - Enter key submits add-feed form; better empty-state hint with example feeds Weather tool: - Accept any city/region name in addition to 'home'/'work'/'all' - Geocodes via Nominatim + fetches live from Open-Meteo for arbitrary queries Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -209,6 +209,30 @@ async function loadTasks() {
|
||||
}
|
||||
}
|
||||
|
||||
const advancingTaskId = ref<number | null>(null);
|
||||
|
||||
const taskStatusNext: Record<string, string> = {
|
||||
todo: "in_progress",
|
||||
in_progress: "done",
|
||||
};
|
||||
|
||||
async function advanceTaskStatus(task: NoteItem, e: Event) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const next = taskStatusNext[task.status ?? ""];
|
||||
if (!next || advancingTaskId.value === task.id) return;
|
||||
advancingTaskId.value = task.id;
|
||||
try {
|
||||
await apiPatch(`/api/notes/${task.id}`, { status: next });
|
||||
const idx = tasks.value.findIndex((t) => t.id === task.id);
|
||||
if (idx !== -1) tasks.value[idx] = { ...tasks.value[idx], status: next };
|
||||
} catch {
|
||||
toast.show("Failed to update task", "error");
|
||||
} finally {
|
||||
advancingTaskId.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadNotes() {
|
||||
notesLoading.value = true;
|
||||
try {
|
||||
@@ -475,9 +499,17 @@ async function confirmDelete() {
|
||||
:class="['task-card', `pri-${task.priority || 'none'}`]"
|
||||
>
|
||||
<span class="task-title">{{ task.title || "Untitled" }}</span>
|
||||
<div v-if="task.priority !== 'none' || task.due_date" class="task-meta">
|
||||
<span v-if="task.priority && task.priority !== 'none'" :class="['priority-dot', `dot-pri-${task.priority}`]" :title="task.priority"></span>
|
||||
<span v-if="task.due_date" class="due-date">{{ task.due_date }}</span>
|
||||
<div class="task-card-footer">
|
||||
<div v-if="task.priority !== 'none' || task.due_date" class="task-meta">
|
||||
<span v-if="task.priority && task.priority !== 'none'" :class="['priority-dot', `dot-pri-${task.priority}`]" :title="task.priority"></span>
|
||||
<span v-if="task.due_date" class="due-date">{{ task.due_date }}</span>
|
||||
</div>
|
||||
<button
|
||||
class="task-advance-btn"
|
||||
title="Move to In Progress"
|
||||
:disabled="advancingTaskId === task.id"
|
||||
@click="advanceTaskStatus(task, $event)"
|
||||
>→</button>
|
||||
</div>
|
||||
</router-link>
|
||||
<p v-if="!group.tasks.filter(t => t.status === 'todo').length" class="col-empty">No tasks</p>
|
||||
@@ -498,9 +530,17 @@ async function confirmDelete() {
|
||||
:class="['task-card', `pri-${task.priority || 'none'}`]"
|
||||
>
|
||||
<span class="task-title">{{ task.title || "Untitled" }}</span>
|
||||
<div v-if="task.priority !== 'none' || task.due_date" class="task-meta">
|
||||
<span v-if="task.priority && task.priority !== 'none'" :class="['priority-dot', `dot-pri-${task.priority}`]" :title="task.priority"></span>
|
||||
<span v-if="task.due_date" class="due-date">{{ task.due_date }}</span>
|
||||
<div class="task-card-footer">
|
||||
<div v-if="task.priority !== 'none' || task.due_date" class="task-meta">
|
||||
<span v-if="task.priority && task.priority !== 'none'" :class="['priority-dot', `dot-pri-${task.priority}`]" :title="task.priority"></span>
|
||||
<span v-if="task.due_date" class="due-date">{{ task.due_date }}</span>
|
||||
</div>
|
||||
<button
|
||||
class="task-advance-btn task-advance-btn--done"
|
||||
title="Mark as Done"
|
||||
:disabled="advancingTaskId === task.id"
|
||||
@click="advanceTaskStatus(task, $event)"
|
||||
>✓</button>
|
||||
</div>
|
||||
</router-link>
|
||||
<p v-if="!group.tasks.filter(t => t.status === 'in_progress').length" class="col-empty">No tasks</p>
|
||||
@@ -1088,7 +1128,26 @@ async function confirmDelete() {
|
||||
.task-card-done .task-title { text-decoration: line-through; }
|
||||
|
||||
.task-title { display: block; font-weight: 500; margin-bottom: 0.2rem; line-height: 1.3; word-break: break-word; }
|
||||
.task-card-footer { display: flex; align-items: center; justify-content: space-between; gap: 0.35rem; min-height: 1.2rem; }
|
||||
.task-meta { display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; }
|
||||
.task-advance-btn {
|
||||
flex-shrink: 0;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
width: 1.4rem; height: 1.4rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 4px;
|
||||
background: transparent;
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.75rem;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s, background 0.15s, color 0.15s;
|
||||
line-height: 1;
|
||||
}
|
||||
.task-card:hover .task-advance-btn { opacity: 1; }
|
||||
.task-advance-btn:hover { background: var(--color-primary); border-color: var(--color-primary); color: #fff; }
|
||||
.task-advance-btn--done:hover { background: var(--color-success, #22c55e); border-color: var(--color-success, #22c55e); color: #fff; }
|
||||
.task-advance-btn:disabled { opacity: 0.4; cursor: default; }
|
||||
|
||||
.priority-dot {
|
||||
width: 7px;
|
||||
|
||||
Reference in New Issue
Block a user