feat(issues): S4a UI — Systems management section in project view
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Failing after 23s
CI & Build / Python tests (push) Successful in 51s
CI & Build / Build & push image (push) Has been skipped

Frontend foundation for Issues + Systems (spec #825, S4a).

- frontend/src/api/systems.ts: typed client (System + list/create/update/delete)
  over /api/projects/<id>/systems, matching the rulebooks api style.
- frontend/src/stores/systems.ts: Pinia store keyed by project (fetch/create/
  update/archive/unarchive/delete), toast-on-error.
- frontend/src/components/SystemsSection.vue: a Systems management section —
  cards (color swatch, name, description, 'N open' issue-count badge) with
  inline create/edit, archive (hidden behind a 'show archived' toggle), and a
  delete-confirm modal. v1 quality: loading skeleton, empty state, error toasts,
  keyboard a11y, focus rings; reuses existing CSS tokens (no new colors).
- ProjectView.vue: new 'Systems' tab (between Notes and Rules), rendering
  <SystemsSection :project-id>, wired like the existing rules tab.

S4b (next) adds issue-editor controls (kind=issue/system multi-select/arose-from),
open-issues lists, and the dashboard surface. NEEDS operator browser verification
(CI typechecks but can't render).

Refs plan 825 (S4a).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-13 23:42:40 -04:00
parent 4f22646c88
commit 4da29562bd
4 changed files with 650 additions and 1 deletions
+8 -1
View File
@@ -7,6 +7,7 @@ import { useTasksStore } from "@/stores/tasks";
import { relativeTime } from "@/composables/useRelativeTime";
import ShareDialog from "@/components/ShareDialog.vue";
import ProjectRulesTab from "@/components/rules/ProjectRulesTab.vue";
import SystemsSection from "@/components/SystemsSection.vue";
import {
LayoutGrid,
Clock,
@@ -87,7 +88,7 @@ async function confirmStartPlanning() {
const saving = ref(false);
const error = ref<string | null>(null);
const activeTab = ref<"tasks" | "notes" | "rules">("tasks");
const activeTab = ref<"tasks" | "notes" | "systems" | "rules">("tasks");
const tasks = ref<NoteItem[]>([]);
const notes = ref<NoteItem[]>([]);
@@ -479,6 +480,9 @@ async function confirmDelete() {
Notes
<span v-if="project.summary" class="tab-count">{{ project.summary.note_count }}</span>
</button>
<button :class="['tab-btn', { active: activeTab === 'systems' }]" @click="activeTab = 'systems'">
Systems
</button>
<button :class="['tab-btn', { active: activeTab === 'rules' }]" @click="activeTab = 'rules'">
Rules
</button>
@@ -663,6 +667,9 @@ async function confirmDelete() {
</template>
</div>
<!-- Systems tab -->
<SystemsSection v-if="activeTab === 'systems'" :project-id="projectId" />
<!-- Rules tab -->
<ProjectRulesTab v-if="activeTab === 'rules'" :project-id="projectId" />
</div>