Setup wizard: plugin display names + GE-Enforce next-steps pointer
All checks were successful
CI / backend (push) Successful in 1m37s
CI / naming (push) Successful in 2s
CI / frontend (push) Successful in 8s

Plugins list now carries a displayname (manifest display_name, else the
machine name title-cased). Adds display_name to the four whose title-case
was wrong: GE-Enforce, USB, Measuring Tools, Knowledge Base. The setup
wizard Features step and Settings > Plugins render it, so "Geenforce"/"Usb"
are gone.

Finish step shows a pointer when GE-Enforce is enabled: it still needs a
scoped service token (Settings > API Tokens) and a share export root
(GE-Enforce page) before the fleet uses it - operational config the wizard
does not collect.

frontend build green; naming green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-07-13 07:41:32 -04:00
parent d7b777a7a0
commit 85a6ab8645
7 changed files with 24 additions and 3 deletions

View File

@@ -41,7 +41,7 @@
<div v-for="p in plugins" :key="p.name" class="plugin-item">
<label class="plugin-row">
<input type="checkbox" :checked="p.enabled" @change="togglePlugin(p, $event.target.checked)" />
<span class="plugin-name">{{ p.name }}</span>
<span class="plugin-name">{{ p.displayname || p.name }}</span>
<span class="plugin-desc">{{ p.description }}</span>
</label>
@@ -140,6 +140,12 @@
<div v-else-if="current.key === 'finish'">
<h2>All set</h2>
<p class="hint">You can change any of this later under Settings. Finish to go to the dashboard.</p>
<div v-if="geenforceEnabled" class="finish-note">
<strong>GE-Enforce is on.</strong> Two more steps before the fleet
uses it: create a service token with the geenforce scopes under
Settings &gt; API Tokens, and set the on-share export root on the
GE-Enforce page.
</div>
</div>
</section>
@@ -235,6 +241,11 @@ function modeOf(plugin) {
return key ? (pluginModes.value[key] || 'selfhosted') : null
}
// GE-Enforce needs post-setup operational config (service token + share root)
// the wizard does not collect; the Finish step points there when it is on.
const geenforceEnabled = computed(() =>
plugins.value.some(p => p.name === 'geenforce' && p.enabled))
// Enabled plugins whose external connection config should be collected right
// now: those in external mode (or with config but no mode concept).
const configurablePlugins = computed(() =>
@@ -420,13 +431,14 @@ async function finish() {
.form-row .form-group { flex: 1; }
.plugin-list { display: flex; flex-direction: column; gap: 0.6rem; }
.plugin-row { display: grid; grid-template-columns: auto auto 1fr; gap: 0.6rem; align-items: baseline; padding: 0.5rem 0.6rem; background: var(--bg); border-radius: 6px; }
.plugin-name { font-weight: 600; text-transform: capitalize; }
.plugin-name { font-weight: 600; }
.plugin-desc { color: var(--text-light); font-size: 0.85rem; }
.plugin-mode { margin: 0.4rem 0 0.2rem 1.9rem; }
.mode-opt { display: block; font-size: 0.86rem; margin-bottom: 0.3rem; cursor: pointer; }
.mode-opt input { margin-right: 0.4rem; }
.plugin-mode .provision-note, .plugin-mode .config-block { margin-left: 0; }
.provision-note { margin: 0.3rem 0 0.2rem 1.9rem; padding: 0.6rem 0.75rem; background: var(--bg); border-left: 3px solid var(--warning); border-radius: 4px; font-size: 0.82rem; }
.finish-note { margin: 0.75rem 0 0; padding: 0.6rem 0.75rem; background: var(--bg); border-left: 3px solid var(--primary); border-radius: 4px; font-size: 0.85rem; }
.provision-note p { margin: 0 0 0.35rem; }
.provision-note p:last-child { margin-bottom: 0; }
.provision-tables code, .provision-docs code { background: var(--bg-card); border: 1px solid var(--border); border-radius: 3px; padding: 0 0.3rem; margin-right: 0.3rem; font-size: 0.78rem; }

View File

@@ -24,7 +24,7 @@
</thead>
<tbody>
<tr v-for="p in plugins" :key="p.name">
<td>{{ p.name }}</td>
<td>{{ p.displayname || p.name }}</td>
<td class="mono">{{ p.version }}</td>
<td class="cell-truncate" :title="p.description">{{ p.description || '-' }}</td>
<td>

View File

@@ -1,5 +1,6 @@
{
"name": "geenforce",
"display_name": "GE-Enforce",
"version": "0.1.0",
"description": "GE-Enforce manifest store. Owns imaging PC-type scopes and their install manifests (apps, scripts, files, registry, version gates) as shopdb data, served to the GE-Enforce client as JSON. Requires GE-Enforce lib >= 2.6 on target PCs (the _CmmVersion gate).",
"author": "ShopDB Team",

View File

@@ -1,5 +1,6 @@
{
"name": "knowledgebase",
"display_name": "Knowledge Base",
"version": "1.0.0",
"description": "Knowledge Base articles linking to external resources",
"author": "ShopDB Team",

View File

@@ -1,5 +1,6 @@
{
"name": "measuringtools",
"display_name": "Measuring Tools",
"version": "1.0.0",
"description": "Metrology and inspection instruments (gauges, calipers, thread gages, bore gages) with a calibration lifecycle and derived calibration status.",
"author": "ShopDB Team",

View File

@@ -1,5 +1,6 @@
{
"name": "usb",
"display_name": "USB",
"version": "1.0.0",
"description": "USB device checkout management",
"author": "ShopDB Team",

View File

@@ -155,6 +155,11 @@ class PluginManager:
manifest = self.loader.load_manifest(name)
available.append({
'name': meta.name,
# Human label for UIs. Manifest display_name wins; else
# title-case the machine name (right for most, e.g.
# "computers" -> "Computers").
'displayname': (manifest.get('display_name')
or meta.name.replace('_', ' ').title()),
'version': meta.version,
'description': meta.description,
'author': meta.author,