feat(design): the starter-role checklist, in the creation UI
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / TypeScript typecheck (push) Successful in 25s
CI & Build / integration (push) Successful in 35s
CI & Build / Python tests (push) Successful in 1m2s
CI & Build / Build & push image (push) Successful in 44s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / TypeScript typecheck (push) Successful in 25s
CI & Build / integration (push) Successful in 35s
CI & Build / Python tests (push) Successful in 1m2s
CI & Build / Build & push image (push) Successful in 44s
Rule #27 — the backend half shipped without a surface an operator can touch, so this is the other half of #2349. StarterRolePicker is a component rather than inline markup because DesignSystemsView has TWO creation forms: the empty state is a sibling branch of the body, not a parent, so a form written into one is unreachable from the other. Inlining the checklist would have made it the next thing in this codebase defined twice and free to drift — which is what the button migration spent nine commits undoing. What it offers is names and purposes, never values. "Named now, valued later": a role you haven't filled shows as to-be-decided, while a role that doesn't exist is what gets written as a literal instead. Every group unchecks individually, and the prefix is editable because `--fs-` is one family's convention, not the product's. Three deliberate details: - All groups checked by DEFAULT, and that default lives in the UI, not the service. create_design_system treats None and [] alike (seed nothing) so it can never write 40 rows into a system whose caller never asked; a UI default is visible and reversible before the click. Different layers, different safe answers. - A failed catalogue fetch is NOT fatal and does not read as an error. Starter roles are an accelerator, not a prerequisite — the form still creates, and the operator adds tokens by hand. - The refs are not cleared after a successful create. The picker owns them and re-seeds on mount; resetting here would race that and silently create the next system with no roles. props + defineEmits rather than defineModel, matching TagInput and the rest of components/. defineModel is available (Vue 3.5) and would be shorter, but being the only file in the codebase using a different binding idiom costs more than the lines it saves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs
This commit is contained in:
@@ -42,6 +42,7 @@ import {
|
||||
import DesignTabs from "@/components/DesignTabs.vue";
|
||||
import { ApiError } from "@/api/client";
|
||||
import { useToastStore } from "@/stores/toast";
|
||||
import StarterRolePicker from "@/components/StarterRolePicker.vue";
|
||||
|
||||
const toast = useToastStore();
|
||||
|
||||
@@ -148,6 +149,10 @@ const newTitle = ref("");
|
||||
const newDescription = ref("");
|
||||
const newParentId = ref<number | null>(null);
|
||||
const creating = ref(false);
|
||||
// Starter roles (#2349). The picker fills these on mount; empty means the
|
||||
// operator unchecked everything, which is a real answer.
|
||||
const starterGroups = ref<string[]>([]);
|
||||
const tokenPrefix = ref("");
|
||||
|
||||
async function submitCreate() {
|
||||
const title = newTitle.value.trim();
|
||||
@@ -158,11 +163,16 @@ async function submitCreate() {
|
||||
title,
|
||||
description: newDescription.value.trim() || undefined,
|
||||
parent_id: newParentId.value,
|
||||
starter_role_groups: starterGroups.value.length ? starterGroups.value : undefined,
|
||||
token_prefix: tokenPrefix.value.trim() || undefined,
|
||||
});
|
||||
newTitle.value = "";
|
||||
newDescription.value = "";
|
||||
newParentId.value = null;
|
||||
showCreate.value = false;
|
||||
// NOT reset: the picker owns these and re-seeds on mount. Clearing them
|
||||
// here would race the next mount and silently create the following system
|
||||
// with no roles at all.
|
||||
await loadSystems();
|
||||
selectedId.value = created.id;
|
||||
toast.show(`Created ${created.title}`);
|
||||
@@ -540,6 +550,10 @@ function isColourish(value: string): boolean {
|
||||
placeholder="What it covers"
|
||||
/>
|
||||
</div>
|
||||
<StarterRolePicker
|
||||
v-model:selected="starterGroups"
|
||||
v-model:prefix="tokenPrefix"
|
||||
/>
|
||||
<div class="row-actions">
|
||||
<button class="btn-primary" :disabled="!newTitle.trim() || creating" @click="submitCreate">
|
||||
{{ creating ? "Creating…" : "Create" }}
|
||||
@@ -601,6 +615,10 @@ function isColourish(value: string): boolean {
|
||||
A system with a parent stores only its differences from it.
|
||||
</p>
|
||||
</div>
|
||||
<StarterRolePicker
|
||||
v-model:selected="starterGroups"
|
||||
v-model:prefix="tokenPrefix"
|
||||
/>
|
||||
<button class="btn-primary" :disabled="!newTitle.trim() || creating" @click="submitCreate">
|
||||
{{ creating ? "Creating…" : "Create" }}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user