feat(issues): S4b frontend — open-issues lists + issue plumbing
CI & Build / Python lint (push) Successful in 5s
CI & Build / TypeScript typecheck (push) Successful in 40s
CI & Build / Python tests (push) Successful in 1m36s
CI & Build / Build & push image (push) Successful in 59s

- types/note.ts: Note gains systems? + arose_from_id?; TaskKind includes 'issue'.
- stores/tasks.ts: create/update accept IssueFields (kind/system_ids/arose_from_id).
- api/systems.ts: getProjectIssues + TaskLike.
- DashboardView.vue: 'Open issues' rail section from dashboard.open_issues
  (links to /tasks/<id>, project + status).
- SystemsSection.vue (project Systems tab): 'Open issues' list via getProjectIssues,
  with system chips, links to /tasks/<id>. Both reuse existing CSS tokens.

Issue-editor controls (kind selector / system multi-select / arose-from picker
in WorkspaceTaskPanel) are the remaining S4b piece. NEEDS operator browser
verification (CI typechecks only).

Refs plan 825 (S4b frontend — lists).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-14 10:50:41 -04:00
parent 79040fe5db
commit 94d32c524a
5 changed files with 101 additions and 5 deletions
+36 -1
View File
@@ -2,7 +2,8 @@
import { ref, computed, onMounted, watch } from "vue";
import { useSystemsStore } from "@/stores/systems";
import { useToastStore } from "@/stores/toast";
import type { System } from "@/api/systems";
import { getProjectIssues } from "@/api/systems";
import type { System, TaskLike } from "@/api/systems";
import { Pencil, Trash2, Archive, ArchiveRestore } from "lucide-vue-next";
const props = defineProps<{ projectId: number }>();
@@ -12,6 +13,7 @@ const toast = useToastStore();
const error = ref<string | null>(null);
const showArchived = ref(false);
const issues = ref<TaskLike[]>([]);
// Create state
const showCreate = ref(false);
@@ -42,6 +44,11 @@ async function load() {
} catch {
error.value = "Failed to load systems.";
}
try {
issues.value = await getProjectIssues(props.projectId);
} catch {
issues.value = [];
}
}
onMounted(load);
@@ -138,6 +145,22 @@ async function confirmDelete() {
<template>
<div class="systems-section">
<!-- Open issues -->
<div v-if="issues.length" class="open-issues">
<div class="open-issues-label"> Open issues ({{ issues.length }})</div>
<ul class="issue-list">
<li v-for="issue in issues" :key="issue.id" class="issue-item">
<router-link :to="`/tasks/${issue.id}`" class="issue-link">
<span class="issue-mark" :class="`imk-${issue.status}`">{{ issue.status === 'in_progress' ? '▸' : '○' }}</span>
<span class="issue-name">{{ issue.title }}</span>
<span v-if="issue.systems && issue.systems.length" class="issue-systems">
<span v-for="s in issue.systems" :key="s.id" class="issue-sys-chip">{{ s.name }}</span>
</span>
</router-link>
</li>
</ul>
</div>
<!-- Toolbar -->
<div class="systems-toolbar">
<button v-if="!showCreate" class="btn-add-system" @click="openCreate">
@@ -300,6 +323,18 @@ async function confirmDelete() {
<style scoped>
.systems-section { display: flex; flex-direction: column; gap: 0.75rem; }
/* ── Open issues ──────────────────────────────────────────────── */
.open-issues { display: flex; flex-direction: column; gap: 0.35rem; }
.open-issues-label { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--color-text-muted); }
.issue-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.2rem; }
.issue-link { display: flex; align-items: center; gap: 0.5rem; padding: 0.35rem 0.5rem; border-radius: var(--radius-sm); text-decoration: none; color: var(--color-text); font-size: 0.85rem; }
.issue-link:hover { background: var(--color-bg-secondary); }
.issue-mark { color: var(--color-text-muted); flex-shrink: 0; }
.issue-mark.imk-in_progress { color: var(--color-primary); }
.issue-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.issue-systems { display: flex; gap: 0.25rem; flex-shrink: 0; flex-wrap: wrap; }
.issue-sys-chip { font-size: 0.66rem; color: var(--color-text-secondary); background: var(--color-bg-secondary); border-radius: 999px; padding: 0.05rem 0.4rem; }
/* ── Toolbar ──────────────────────────────────────────────────── */
.systems-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; }
.btn-add-system {