Make a partly-undone import retryable, and refuse a plan it cannot deliver
This commit is contained in:
@@ -1323,13 +1323,33 @@ td.actions .btn + .btn {
|
||||
gap: 0.75rem;
|
||||
margin-top: 0.4rem;
|
||||
}
|
||||
/* A branding preview has to show a logo whose colour we do not control. The
|
||||
shipped GE mark is fill="white" because it lives on the dark sidebar, so on a
|
||||
plain white tile it rendered white-on-white and read as a failed load. A
|
||||
checker ground makes any logo visible - light, dark or transparent - and is
|
||||
the usual convention for previewing artwork with alpha. */
|
||||
.map-thumb {
|
||||
height: 40px;
|
||||
min-width: 40px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
/* MID-TONE, not white and not near-white. The tile has to show artwork whose
|
||||
colour we do not control: the shipped GE mark is fill="white" for the dark
|
||||
sidebar, the monogram is fill="black". A light ground hides the first, a
|
||||
dark ground hides the second, so the checker sits either side of mid grey
|
||||
and both read against it. */
|
||||
background-color: #9aa1ad;
|
||||
background-image:
|
||||
linear-gradient(45deg, #6f7783 25%, transparent 25%, transparent 75%, #6f7783 75%),
|
||||
linear-gradient(45deg, #6f7783 25%, transparent 25%, transparent 75%, #6f7783 75%);
|
||||
background-size: 12px 12px;
|
||||
background-position: 0 0, 6px 6px;
|
||||
padding: 2px;
|
||||
}
|
||||
.map-thumb-dark {
|
||||
background-color: #222;
|
||||
background-image: none;
|
||||
}
|
||||
.map-thumb-dark { background: #222; }
|
||||
.settings-success {
|
||||
margin-top: 1rem;
|
||||
padding: 0.75rem;
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
>
|
||||
<div class="map-upload-row">
|
||||
<input type="file" :accept="logo.accept" @change="uploadLogo(logo.kind, logo.key, $event)" :disabled="brandingUploading" />
|
||||
<img v-if="settings[logo.key]" :src="settings[logo.key]" class="map-thumb" :alt="logo.label" />
|
||||
<img v-if="settings[logo.key]" :src="withBase(settings[logo.key])" class="map-thumb" :alt="logo.label" />
|
||||
</div>
|
||||
<small class="input-hint">{{ logo.hint }}</small>
|
||||
</label>
|
||||
@@ -132,6 +132,12 @@
|
||||
<script setup>
|
||||
import { onMounted } from 'vue'
|
||||
import { useSystemSettings, brandingLogos } from '../../composables/systemSettings'
|
||||
// The stored value is root-relative ('/ge-aerospace-logo.svg'). This app is
|
||||
// mounted under a base path at every real deployment (/shopdb, /ops), so a raw
|
||||
// src resolves against the SERVER root and 404s - which is what "error loading
|
||||
// image" was. siteSettings.js already wraps every logo this way for the rest of
|
||||
// the app; this preview was the one place that did not.
|
||||
import { withBase } from '@/utils/basePath'
|
||||
|
||||
const {
|
||||
settings, saving, brandingUploading, error, success,
|
||||
|
||||
@@ -26,6 +26,14 @@
|
||||
<button class="btn btn-small" @click="startAddLevel(building)">Add level</button>
|
||||
</div>
|
||||
|
||||
<!-- Scroll rather than squeeze: nine columns of inputs do not fit a
|
||||
narrow window, and without a wrapper the table ran off the right of
|
||||
the page with the cells crushed out of shape.
|
||||
NOT the global .table-container - that also sets white-space: nowrap,
|
||||
and the sizing hint below is a full sentence, so nowrap made the
|
||||
table WIDER instead of letting the paragraph wrap. Scroll here, wrap
|
||||
inside the cells. -->
|
||||
<div class="level-table-scroll">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -46,7 +54,7 @@
|
||||
<td>
|
||||
<input
|
||||
v-model="level.levelname"
|
||||
class="form-control"
|
||||
class="form-control level-name-input"
|
||||
@blur="renameLevel(level)"
|
||||
:disabled="saving"
|
||||
/>
|
||||
@@ -123,6 +131,7 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add building -->
|
||||
@@ -437,6 +446,17 @@ async function remove(level) {
|
||||
}
|
||||
|
||||
.mono { font-family: monospace; }
|
||||
/* Wide enough that every column keeps a usable width; past this the wrapper
|
||||
scrolls instead of the inputs collapsing to a few pixels. */
|
||||
.level-table-scroll { overflow-x: auto; }
|
||||
.data-table { min-width: 58rem; }
|
||||
/* The sizing note is prose and must wrap; it is the reason the shared
|
||||
table-container wrapper is wrong for this table. */
|
||||
.data-table .input-hint { white-space: normal; display: block; max-width: 22rem; }
|
||||
.data-table td, .data-table th { vertical-align: top; }
|
||||
/* A bare form-control in a table cell collapses to whatever width is left over,
|
||||
which put "Ground floor" in a box one character wide. */
|
||||
.level-name-input { width: 11rem; }
|
||||
.order-input { width: 4.5rem; }
|
||||
.size-input { width: 6rem; }
|
||||
.size-fields { display: flex; align-items: center; gap: 0.35rem; }
|
||||
|
||||
@@ -269,7 +269,7 @@
|
||||
below if needed.
|
||||
</template>
|
||||
<template v-else-if="wincReport.blocking">
|
||||
{{ wincReport.rowcount }} row(s) in scope, but unresolved values
|
||||
{{ wincReport.rowcount }} row(s) in scope, but the problems below
|
||||
block the import - nothing was written. Resolve them and preview
|
||||
again.
|
||||
</template>
|
||||
@@ -314,6 +314,29 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Blocking and NOT acknowledgeable, unlike needscreate above.
|
||||
The packet blocks on these, so the page must name them or the
|
||||
banner points at nothing the admin can see. -->
|
||||
<div
|
||||
v-if="wincReport.uncreatable && Object.keys(wincReport.uncreatable).length"
|
||||
class="problems"
|
||||
>
|
||||
<h4>Rows this import cannot create (blocking)</h4>
|
||||
<p class="muted">
|
||||
The export needs these, but this import only ever creates business
|
||||
units. Create them in Settings first, or alias the export values
|
||||
to rows that already exist, then preview again.
|
||||
</p>
|
||||
<ul>
|
||||
<li v-for="(items, group) in wincReport.uncreatable" :key="group">
|
||||
<strong>{{ group }}:</strong>
|
||||
<span v-for="(count, name) in items" :key="name">
|
||||
"{{ name }}" ({{ count }}){{ ' ' }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-if="wincReport.unresolved && Object.keys(wincReport.unresolved).length" class="problems">
|
||||
<h4>Unresolved values (blocking)</h4>
|
||||
<p class="muted">
|
||||
@@ -354,15 +377,27 @@
|
||||
<td>{{ b.createdat }}</td>
|
||||
<td>{{ b.createdby || '-' }}</td>
|
||||
<td>{{ b.rowcount }}</td>
|
||||
<td>{{ b.status }}</td>
|
||||
<td>
|
||||
{{ b.status }}
|
||||
<small v-if="b.status === 'partially-undone'" class="muted">
|
||||
- some rows could not be put back
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
<!-- 'partially-undone' is retryable and MUST offer the
|
||||
button: that status exists because an undo stopped at a
|
||||
conflict, and retrying once the conflict is resolved is
|
||||
the whole point. Testing only for 'applied' left the
|
||||
backend willing and the button hidden. -->
|
||||
<button
|
||||
v-if="b.status === 'applied'"
|
||||
v-if="b.status === 'applied' || b.status === 'partially-undone'"
|
||||
class="btn btn-secondary btn-sm"
|
||||
:disabled="wincUndoingId === b.batchid"
|
||||
@click="undoWincBatch(b.batchid)"
|
||||
>
|
||||
{{ wincUndoingId === b.batchid ? 'Undoing...' : 'Undo' }}
|
||||
{{ wincUndoingId === b.batchid
|
||||
? 'Undoing...'
|
||||
: (b.status === 'partially-undone' ? 'Retry undo' : 'Undo') }}
|
||||
</button>
|
||||
<span v-else class="muted">undone</span>
|
||||
</td>
|
||||
|
||||
Reference in New Issue
Block a user