Separate shipped policies from site policies in the list
The list mixed both kinds of configuration policy into one table, so the only way to tell a shipped default from something the site had written was to read the source column. Split them into two tables under their own headings. The split is presentational only. Both tables render the same Edit and Delete buttons gated on the same iseditable(p) rule, so a shipped policy is not stranded behind a dead button the way it was when the buttons gated on source === 'local'. The spec counted exactly two disabled bindings, which the second table would have broken while the behaviour it was guarding stayed correct. It now counts Edit and Delete in pairs and asserts the two always agree, so adding a group does not fail a test about button gating.
This commit is contained in:
@@ -12,7 +12,21 @@
|
||||
has been configured.</p>
|
||||
</div>
|
||||
|
||||
<!-- Split into two cards rather than one list with a Source column.
|
||||
There is one shipped policy PER MONITOR, so shipped rows outnumber
|
||||
site-authored ones and a single sorted list buries the handful of
|
||||
policies the site actually wrote. The split also gives the shipped
|
||||
group somewhere to carry its warning, which a per-row badge cannot
|
||||
do. Both groups render the same row markup and the same editable
|
||||
rule - the separation is presentational, not a permission boundary. -->
|
||||
<div class="card">
|
||||
<h3>Shipped policies</h3>
|
||||
<p class="shipped-warning">
|
||||
These ship with the product as starting points - one per monitor. You
|
||||
can edit or delete them and your changes are kept, but an upgrade may
|
||||
add new rows here. Deleting one does not stop the monitor it refers to;
|
||||
it only removes this policy's opinion about it.
|
||||
</p>
|
||||
<div v-if="loading" class="loading">Loading...</div>
|
||||
<template v-else>
|
||||
<div class="table-container">
|
||||
@@ -27,7 +41,49 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="p in policies" :key="p.configurationpolicyid">
|
||||
<tr v-for="p in shippedPolicies" :key="p.configurationpolicyid">
|
||||
<td>{{ p.name }}</td>
|
||||
<td class="cell-truncate" :title="p.description">{{ p.description || '-' }}</td>
|
||||
<td>
|
||||
<span v-for="a in p.actions" :key="a.action" class="badge" style="margin-right:4px;">
|
||||
{{ actionLabel(a.action) }} ({{ a.mode }})
|
||||
</span>
|
||||
<span v-if="!p.actions || p.actions.length === 0" style="color: var(--text-light);">none</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-warning">{{ p.source }}</span>
|
||||
</td>
|
||||
<td class="actions">
|
||||
<button class="btn btn-secondary btn-sm" :disabled="!iseditable(p)" @click="openModal(p)">Edit</button>
|
||||
<button class="btn btn-danger btn-sm" :disabled="!iseditable(p)" @click="remove(p)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="shippedPolicies.length === 0">
|
||||
<td colspan="5" style="text-align:center; color: var(--text-light);">No shipped policies</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>Site policies</h3>
|
||||
<div v-if="loading" class="loading">Loading...</div>
|
||||
<template v-else>
|
||||
<div class="table-container">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Actions in policy</th>
|
||||
<th>Source</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="p in localPolicies" :key="p.configurationpolicyid">
|
||||
<td>{{ p.name }}</td>
|
||||
<td class="cell-truncate" :title="p.description">{{ p.description || '-' }}</td>
|
||||
<td>
|
||||
@@ -46,7 +102,7 @@
|
||||
<button class="btn btn-danger btn-sm" :disabled="!iseditable(p)" @click="remove(p)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="policies.length === 0">
|
||||
<tr v-if="localPolicies.length === 0">
|
||||
<td colspan="5" style="text-align:center; color: var(--text-light);">No configuration policies</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -115,7 +171,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { computersApi, applicationsApi, endpointsApi } from '@/api'
|
||||
import { useToast } from '@/composables/toast'
|
||||
import { apiError } from '@/utils/apiError'
|
||||
@@ -299,6 +355,17 @@ function iseditable(p) {
|
||||
return EDITABLE_SOURCES.includes(p.source)
|
||||
}
|
||||
|
||||
// Presentational split only. Anything that is not 'shipped' lands in the site
|
||||
// list - including 'autoapp', which is generated rather than hand-written but
|
||||
// is still this site's own row and carries its own badge there. Written as
|
||||
// "not shipped" rather than "=== local" so a future source value shows up in a
|
||||
// list instead of vanishing from both, which is the failure mode that made the
|
||||
// old Edit buttons dead.
|
||||
const shippedPolicies = computed(
|
||||
() => policies.value.filter(p => p.source === 'shipped'))
|
||||
const localPolicies = computed(
|
||||
() => policies.value.filter(p => p.source !== 'shipped'))
|
||||
|
||||
async function remove(p) {
|
||||
if (!confirm(`Delete configuration policy "${p.name}"?`)) return
|
||||
try {
|
||||
@@ -318,6 +385,14 @@ async function remove(p) {
|
||||
.feature-off-notice p {
|
||||
margin: 0.4rem 0 0 0;
|
||||
}
|
||||
/* Matches the feature-off notice rather than inventing a second warning
|
||||
treatment: same left rule, same warning colour. */
|
||||
.shipped-warning {
|
||||
border-left: 3px solid var(--warning);
|
||||
padding-left: 0.75rem;
|
||||
margin: 0 0 1rem 0;
|
||||
color: var(--text-light);
|
||||
}
|
||||
.modal-lg {
|
||||
max-width: 760px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user