fix(review): UI nits — margins, buttons, moisture, live validation
Post-publish: hide Back while publishing; drop Settings/Slabs chrome on done (remove unused white Slabs control). Review/App horizontal padding. Unify Create desc / View prompt / Create title|short as .btn-sec. Moisture kd default: sync select after form mount; persist via commit. Ready/missing update on input (debounced) without blur.
This commit is contained in:
+16
-13
@@ -11,7 +11,6 @@
|
|||||||
import MaskPanel from './capture/MaskPanel.svelte';
|
import MaskPanel from './capture/MaskPanel.svelte';
|
||||||
import MeasurePanel from './capture/MeasurePanel.svelte';
|
import MeasurePanel from './capture/MeasurePanel.svelte';
|
||||||
import Settings from './create/settings/Settings.svelte';
|
import Settings from './create/settings/Settings.svelte';
|
||||||
import SlabList from './create/slabs/SlabList.svelte';
|
|
||||||
import Review from './create/review/Review.svelte';
|
import Review from './create/review/Review.svelte';
|
||||||
import Publish from './create/publish/Publish.svelte';
|
import Publish from './create/publish/Publish.svelte';
|
||||||
import {
|
import {
|
||||||
@@ -27,7 +26,6 @@
|
|||||||
let busy = false;
|
let busy = false;
|
||||||
let pngUrl: string | null = null;
|
let pngUrl: string | null = null;
|
||||||
let showSettings = false;
|
let showSettings = false;
|
||||||
let showSlabs = false;
|
|
||||||
|
|
||||||
let curFlow: import('./create/flow').Flow = { phase: 'capture' };
|
let curFlow: import('./create/flow').Flow = { phase: 'capture' };
|
||||||
$: curFlow = $flow;
|
$: curFlow = $flow;
|
||||||
@@ -238,16 +236,14 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Separate pages, never in the flow (UX.md §1). -->
|
<!-- Settings only (dev Slabs list removed — unused white control). Hidden after publish. -->
|
||||||
<div style="margin-top:1rem;display:flex;gap:8px">
|
{#if curFlow.phase !== 'done'}
|
||||||
<button on:click={() => (showSettings = !showSettings)} data-testid="open-settings">Settings</button>
|
<div class="app-tools">
|
||||||
<button on:click={() => (showSlabs = !showSlabs)} data-testid="open-slabs">Slabs</button>
|
<button type="button" class="tool" on:click={() => (showSettings = !showSettings)} data-testid="open-settings">Settings</button>
|
||||||
</div>
|
</div>
|
||||||
{#if showSettings}
|
|
||||||
<Settings />
|
|
||||||
{/if}
|
{/if}
|
||||||
{#if showSlabs}
|
{#if showSettings && curFlow.phase !== 'done'}
|
||||||
<SlabList />
|
<Settings />
|
||||||
{/if}
|
{/if}
|
||||||
</main>
|
</main>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -258,17 +254,24 @@
|
|||||||
background: #121212;
|
background: #121212;
|
||||||
font-family: system-ui, -apple-system, Segoe UI, sans-serif;
|
font-family: system-ui, -apple-system, Segoe UI, sans-serif;
|
||||||
}
|
}
|
||||||
.app, .hardstop { max-width: 720px; margin: 0 auto; min-height: 100vh; }
|
.app, .hardstop { max-width: 720px; margin: 0 auto; min-height: 100vh; box-sizing: border-box; }
|
||||||
|
.app { padding: 12px 16px 24px; }
|
||||||
.hardstop { padding: 48px 24px; color: #eee; text-align: center; }
|
.hardstop { padding: 48px 24px; color: #eee; text-align: center; }
|
||||||
.hardstop h1 { font-size: 1.6rem; }
|
.hardstop h1 { font-size: 1.6rem; }
|
||||||
.detail { color: #888; font-size: 0.85rem; }
|
.detail { color: #888; font-size: 0.85rem; }
|
||||||
.panel { padding: 16px; color: #eee; }
|
.panel { padding: 4px 0 16px; color: #eee; }
|
||||||
.done .ok { color: #7fd37f; font-weight: 600; }
|
.done .ok { color: #7fd37f; font-weight: 600; }
|
||||||
.row { display: flex; gap: 10px; margin-top: 16px; }
|
.row { display: flex; gap: 10px; margin-top: 16px; }
|
||||||
.primary {
|
.primary {
|
||||||
flex: 1; padding: 14px; font-size: 1.05rem; border-radius: 10px; border: 0;
|
flex: 1; padding: 14px; font-size: 1rem; border-radius: 3px; border: 0;
|
||||||
background: #00e5ff; color: #002; font-weight: 700;
|
background: #00e5ff; color: #002; font-weight: 700;
|
||||||
}
|
}
|
||||||
.busy { color: #aaa; }
|
.busy { color: #aaa; }
|
||||||
.save-err { margin-top: 12px; color: #ffb4a2; font-weight: 600; }
|
.save-err { margin-top: 12px; color: #ffb4a2; font-weight: 600; }
|
||||||
|
.app-tools { margin-top: 1rem; display: flex; gap: 8px; }
|
||||||
|
.tool {
|
||||||
|
box-sizing: border-box; padding: 8px 12px; border-radius: 3px; border: 1px solid #444;
|
||||||
|
background: #1d1d1d; color: #ccc; font-size: .82rem; font-weight: 600; cursor: pointer;
|
||||||
|
}
|
||||||
|
.tool:hover { background: #242424; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -77,11 +77,15 @@
|
|||||||
<h1>Publish to WooCommerce</h1>
|
<h1>Publish to WooCommerce</h1>
|
||||||
<p class="meta">SKU {slab?.sku || '—'} · {slab?.bdft != null ? slab.bdft.toFixed(1) : '—'} bdft · {slab?.price != null ? `$${slab.price.toFixed(2)}` : '—'}</p>
|
<p class="meta">SKU {slab?.sku || '—'} · {slab?.bdft != null ? slab.bdft.toFixed(1) : '—'} bdft · {slab?.price != null ? `$${slab.price.toFixed(2)}` : '—'}</p>
|
||||||
|
|
||||||
{#if st.phase === 'idle' || st.phase === 'working'}
|
{#if st.phase === 'idle'}
|
||||||
<button type="button" class="primary" data-testid="publish-btn" on:click={doPublish} disabled={st.phase === 'working'}>
|
<button type="button" class="primary" data-testid="publish-btn" on:click={doPublish}>
|
||||||
{st.phase === 'working' ? 'Publishing…' : 'Publish'}
|
Publish
|
||||||
|
</button>
|
||||||
|
<button type="button" class="ghost" data-testid="back-review" on:click={backToReview}>Back to Review</button>
|
||||||
|
{:else if st.phase === 'working'}
|
||||||
|
<button type="button" class="primary" data-testid="publish-btn" disabled>
|
||||||
|
Publishing…
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="ghost" data-testid="back-review" on:click={backToReview} disabled={st.phase === 'working'}>Back to Review</button>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if st.phase === 'success'}
|
{#if st.phase === 'success'}
|
||||||
@@ -119,7 +123,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.pub { max-width: 520px; margin: 0 auto; color: #eee; }
|
.pub { max-width: 520px; margin: 0 auto; color: #eee; padding: 4px 4px 16px; box-sizing: border-box; }
|
||||||
h1 { font-size: 1.3rem; margin: 0 0 6px; } h2 { font-size: 1.1rem; margin: 0 0 6px; }
|
h1 { font-size: 1.3rem; margin: 0 0 6px; } h2 { font-size: 1.1rem; margin: 0 0 6px; }
|
||||||
.meta { color: #888; font-size: .82rem; margin: 4px 0 10px; }
|
.meta { color: #888; font-size: .82rem; margin: 4px 0 10px; }
|
||||||
.primary { width: 100%; padding: 14px; font-size: 1rem; border-radius: 3px; border: 0; background: #00e5ff; color: #002; font-weight: 700; margin-top: 12px; }
|
.primary { width: 100%; padding: 14px; font-size: 1rem; border-radius: 3px; border: 0; background: #00e5ff; color: #002; font-weight: 700; margin-top: 12px; }
|
||||||
|
|||||||
@@ -163,6 +163,23 @@
|
|||||||
// a stale "not ready" review clobbers the final ready one — the
|
// a stale "not ready" review clobbers the final ready one — the
|
||||||
// continue-to-Publish button stays disabled even though the status reads ready.
|
// continue-to-Publish button stays disabled even though the status reads ready.
|
||||||
let commitToken = 0;
|
let commitToken = 0;
|
||||||
|
// Live ready/missing: commit on input/change without requiring blur (extend Create-desc gate).
|
||||||
|
let fieldCommitTimer: ReturnType<typeof setTimeout> | undefined;
|
||||||
|
let descCommitTimer: ReturnType<typeof setTimeout> | undefined;
|
||||||
|
function scheduleFieldCommit() {
|
||||||
|
if (fieldCommitTimer) clearTimeout(fieldCommitTimer);
|
||||||
|
fieldCommitTimer = setTimeout(() => {
|
||||||
|
fieldCommitTimer = undefined;
|
||||||
|
void commitReview();
|
||||||
|
}, 250);
|
||||||
|
}
|
||||||
|
function scheduleDescCommit() {
|
||||||
|
if (descCommitTimer) clearTimeout(descCommitTimer);
|
||||||
|
descCommitTimer = setTimeout(() => {
|
||||||
|
descCommitTimer = undefined;
|
||||||
|
void commitDescription();
|
||||||
|
}, 250);
|
||||||
|
}
|
||||||
|
|
||||||
// --- Inference ---
|
// --- Inference ---
|
||||||
let genBusy = false;
|
let genBusy = false;
|
||||||
@@ -257,6 +274,8 @@
|
|||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
for (const u of photoUrls) URL.revokeObjectURL(u);
|
for (const u of photoUrls) URL.revokeObjectURL(u);
|
||||||
photoUrls = [];
|
photoUrls = [];
|
||||||
|
if (fieldCommitTimer) clearTimeout(fieldCommitTimer);
|
||||||
|
if (descCommitTimer) clearTimeout(descCommitTimer);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Whole-card tap = keep/remove toggle. A real drag (>6px) reorders and does NOT
|
// Whole-card tap = keep/remove toggle. A real drag (>6px) reorders and does NOT
|
||||||
@@ -343,7 +362,7 @@
|
|||||||
edgeSel = s.edge_type_term_id != null ? String(s.edge_type_term_id) : '';
|
edgeSel = s.edge_type_term_id != null ? String(s.edge_type_term_id) : '';
|
||||||
figureIds = [...(s.figure_term_ids ?? [])];
|
figureIds = [...(s.figure_term_ids ?? [])];
|
||||||
gradeSel = s.grade_term_id != null ? String(s.grade_term_id) : '';
|
gradeSel = s.grade_term_id != null ? String(s.grade_term_id) : '';
|
||||||
// Moisture default Kiln dried (slug kd) when empty — persist via commitReview after load.
|
// Moisture default Kiln dried (slug kd) when empty — mount commitReview persists it.
|
||||||
{
|
{
|
||||||
const moistAttr = t?.attributes.find((a) => a.name.toLowerCase() === 'moisture');
|
const moistAttr = t?.attributes.find((a) => a.name.toLowerCase() === 'moisture');
|
||||||
const moistTerms = (moistAttr?.terms ?? []).map((x) => ({
|
const moistTerms = (moistAttr?.terms ?? []).map((x) => ({
|
||||||
@@ -351,6 +370,8 @@
|
|||||||
}));
|
}));
|
||||||
const applied = applyMoistureDefault(s.moisture_term_id, moistTerms);
|
const applied = applyMoistureDefault(s.moisture_term_id, moistTerms);
|
||||||
moistureSel = applied === '' ? '' : String(applied);
|
moistureSel = applied === '' ? '' : String(applied);
|
||||||
|
// Keep native <select> in sync when we programmatically default empty → kd.
|
||||||
|
void syncSelects();
|
||||||
}
|
}
|
||||||
featTagIds = [...(s.feat_tag_ids ?? [])];
|
featTagIds = [...(s.feat_tag_ids ?? [])];
|
||||||
title = s.title ?? '';
|
title = s.title ?? '';
|
||||||
@@ -650,6 +671,8 @@
|
|||||||
loadError = e instanceof ApiError ? e.message : 'Could not load the slab.';
|
loadError = e instanceof ApiError ? e.message : 'Could not load the slab.';
|
||||||
} finally {
|
} finally {
|
||||||
loading = false;
|
loading = false;
|
||||||
|
// Form mounts after loading flips false — sync selects (incl. moisture kd default).
|
||||||
|
void syncSelects();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -833,8 +856,8 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="lbl" for="title">Title</label>
|
<label class="lbl" for="title">Title</label>
|
||||||
<div class="create-row">
|
<div class="create-row">
|
||||||
<input id="title" class="inp" data-testid="title" bind:value={title} />
|
<input id="title" class="inp" data-testid="title" bind:value={title} on:input={scheduleFieldCommit} />
|
||||||
<button type="button" class="create-btn" data-testid="create-title"
|
<button type="button" class="btn-sec" data-testid="create-title"
|
||||||
disabled={!canCreateTitle || createBusy} on:click={createTitle}>
|
disabled={!canCreateTitle || createBusy} on:click={createTitle}>
|
||||||
{createBusy ? 'Building…' : 'Create title'}
|
{createBusy ? 'Building…' : 'Create title'}
|
||||||
</button>
|
</button>
|
||||||
@@ -843,8 +866,8 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="lbl" for="short-desc">Short description</label>
|
<label class="lbl" for="short-desc">Short description</label>
|
||||||
<div class="create-row">
|
<div class="create-row">
|
||||||
<input id="short-desc" class="inp" data-testid="short-desc" bind:value={shortDescription} />
|
<input id="short-desc" class="inp" data-testid="short-desc" bind:value={shortDescription} on:input={scheduleFieldCommit} />
|
||||||
<button type="button" class="create-btn" data-testid="create-short-desc"
|
<button type="button" class="btn-sec" data-testid="create-short-desc"
|
||||||
disabled={!canCreateTitle || createBusy} on:click={createTitle}>
|
disabled={!canCreateTitle || createBusy} on:click={createTitle}>
|
||||||
{createBusy ? 'Building…' : 'Create short desc'}
|
{createBusy ? 'Building…' : 'Create short desc'}
|
||||||
</button>
|
</button>
|
||||||
@@ -852,11 +875,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="lbl" for="desc">Description</label>
|
<label class="lbl" for="desc">Description</label>
|
||||||
<textarea id="desc" class="inp ta" data-testid="desc" rows="3" bind:value={description} on:blur={commitDescription}></textarea>
|
<textarea id="desc" class="inp ta" data-testid="desc" rows="3" bind:value={description} on:input={scheduleDescCommit} on:blur={commitDescription}></textarea>
|
||||||
<div class="gen">
|
<div class="gen">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="gen-btn"
|
class="btn-sec"
|
||||||
data-testid="create-desc"
|
data-testid="create-desc"
|
||||||
on:click={generateContent}
|
on:click={generateContent}
|
||||||
disabled={!canCreateDesc || genBusy}
|
disabled={!canCreateDesc || genBusy}
|
||||||
@@ -866,7 +889,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="view-prompt-btn"
|
class="btn-sec"
|
||||||
data-testid="view-prompt"
|
data-testid="view-prompt"
|
||||||
on:click={() => { if (viewPromptOpen) viewPromptOpen = false; else void loadViewPrompt(); }}
|
on:click={() => { if (viewPromptOpen) viewPromptOpen = false; else void loadViewPrompt(); }}
|
||||||
disabled={viewPromptBusy}
|
disabled={viewPromptBusy}
|
||||||
@@ -917,11 +940,11 @@
|
|||||||
<label class="lbl" for="price">Price ($)
|
<label class="lbl" for="price">Price ($)
|
||||||
{#if review?.price_suggestion != null}<span class="sug">suggested ${review.price_suggestion.toFixed(2)}</span>{/if}
|
{#if review?.price_suggestion != null}<span class="sug">suggested ${review.price_suggestion.toFixed(2)}</span>{/if}
|
||||||
</label>
|
</label>
|
||||||
<input id="price" class="inp" data-testid="price" type="number" step="0.01" bind:value={price} on:change={commitReview} />
|
<input id="price" class="inp" data-testid="price" type="number" step="0.01" bind:value={price} on:input={scheduleFieldCommit} on:change={commitReview} />
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="lbl" for="sku">SKU <span class="sug">last field, 3–24 chars</span></label>
|
<label class="lbl" for="sku">SKU <span class="sug">last field, 3–24 chars</span></label>
|
||||||
<input id="sku" class="inp" data-testid="sku" placeholder="SLAB-…" bind:value={sku} on:blur={commitReview} />
|
<input id="sku" class="inp" data-testid="sku" placeholder="SLAB-…" bind:value={sku} on:input={scheduleFieldCommit} on:blur={commitReview} />
|
||||||
{#if sku && !/^[A-Za-z0-9-]{3,24}$/.test(sku)}
|
{#if sku && !/^[A-Za-z0-9-]{3,24}$/.test(sku)}
|
||||||
<p class="nudge">SKU must be 3–24 alphanumerics or hyphens.</p>
|
<p class="nudge">SKU must be 3–24 alphanumerics or hyphens.</p>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -961,7 +984,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.review { max-width: 560px; margin: 0 auto; color: #eee; }
|
.review { max-width: 560px; margin: 0 auto; color: #eee; padding: 4px 4px 20px; box-sizing: border-box; }
|
||||||
h1 { font-size: 1.3rem; margin: 0 0 12px; }
|
h1 { font-size: 1.3rem; margin: 0 0 12px; }
|
||||||
.math div { display: flex; justify-content: space-between; border-bottom: 1px solid #2a2a2a; padding: 5px 0; font-size: .85rem; }
|
.math div { display: flex; justify-content: space-between; border-bottom: 1px solid #2a2a2a; padding: 5px 0; font-size: .85rem; }
|
||||||
.math dt { color: #9a9a9a; margin: 0; } .math dd { margin: 0; font-variant-numeric: tabular-nums; }
|
.math dt { color: #9a9a9a; margin: 0; } .math dd { margin: 0; font-variant-numeric: tabular-nums; }
|
||||||
@@ -976,15 +999,25 @@
|
|||||||
.field { margin-bottom: 12px; }
|
.field { margin-bottom: 12px; }
|
||||||
.nudge { color: #e8a; font-size: .74rem; margin: 4px 0 0; }
|
.nudge { color: #e8a; font-size: .74rem; margin: 4px 0 0; }
|
||||||
.sug { color: #aaa; font-size: .74rem; font-weight: 400; margin-left: 6px; }
|
.sug { color: #aaa; font-size: .74rem; font-weight: 400; margin-left: 6px; }
|
||||||
.gen { margin-top: 8px; } .gen-btn { padding: 8px 12px; border: 0; border-radius: 3px; background: #00e5ff; color: #002; font-weight: 600; font-size: .82rem; }
|
.gen { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 8px; }
|
||||||
/* Lock chrome matches agreed #90 wireframe: muted dark green, not jarring neon. */
|
/* Unified secondary actions: Create title/short/desc, View prompt — same size & label weight. */
|
||||||
/* #89: explicit "Create" build buttons beside the title / short-desc fields. */
|
|
||||||
.create-row { display: flex; gap: 8px; align-items: stretch; }
|
.create-row { display: flex; gap: 8px; align-items: stretch; }
|
||||||
.create-row .inp { flex: 1; }
|
.create-row .inp { flex: 1; }
|
||||||
.create-btn { box-sizing: border-box; padding: 8px 12px; border: 1px solid #333; border-radius: 3px;
|
.btn-sec { box-sizing: border-box; padding: 9px 12px; border: 1px solid #444; border-radius: 3px;
|
||||||
background: #1d1d1d; color: #eee; font-size: .82rem; font-weight: 600; cursor: pointer; white-space: nowrap; }
|
background: #1d1d1d; color: #eee; font-size: .82rem; font-weight: 600; cursor: pointer; white-space: nowrap; }
|
||||||
.create-btn:hover { background: #242424; }
|
.btn-sec:hover { background: #242424; }
|
||||||
.create-btn:disabled { background: #222; color: #666; border-color: #333; cursor: not-allowed; }
|
.btn-sec:disabled { background: #222; color: #666; border-color: #333; cursor: not-allowed; }
|
||||||
|
.prompt-panel { margin-top: 10px; border: 1px solid #333; border-radius: 3px; background: #141414; padding: 10px 12px; }
|
||||||
|
.prompt-head { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 8px; }
|
||||||
|
.prompt-title { font-size: .78rem; color: #9a9a9a; flex: 1; min-width: 120px; }
|
||||||
|
.mini { box-sizing: border-box; padding: 6px 10px; border: 1px solid #444; border-radius: 3px;
|
||||||
|
background: #1d1d1d; color: #ccc; font-size: .74rem; font-weight: 600; cursor: pointer; }
|
||||||
|
.mini:disabled { color: #666; cursor: not-allowed; }
|
||||||
|
.prompt-text { margin: 0; padding: 10px; background: #0f0f0f; border: 1px solid #2a2a2a; border-radius: 3px;
|
||||||
|
color: #ddd; font-size: .72rem; line-height: 1.45; white-space: pre-wrap; word-break: break-word; max-height: 280px; overflow: auto; }
|
||||||
|
.prompt-photos { margin-top: 10px; }
|
||||||
|
.prompt-photos-lbl { font-size: .72rem; color: #888; margin-bottom: 4px; }
|
||||||
|
.prompt-photos ul { margin: 0; padding-left: 18px; color: #aaa; font-size: .72rem; }
|
||||||
|
|
||||||
/* Primary wood category — inline under wood MultiSelect (REVIEW-15 / #90 wireframe). */
|
/* Primary wood category — inline under wood MultiSelect (REVIEW-15 / #90 wireframe). */
|
||||||
.primary-set { margin-top: 8px; padding: 10px 12px; border: 1px solid #333; border-radius: 3px; background: #141414; }
|
.primary-set { margin-top: 8px; padding: 10px 12px; border: 1px solid #333; border-radius: 3px; background: #141414; }
|
||||||
@@ -1003,9 +1036,9 @@
|
|||||||
.status.ok { background: #122a12; color: #7fd37f; }
|
.status.ok { background: #122a12; color: #7fd37f; }
|
||||||
.missing { color: #999; font-size: .76rem; margin: 6px 0 0; }
|
.missing { color: #999; font-size: .76rem; margin: 6px 0 0; }
|
||||||
.row { display: flex; gap: 10px; margin-top: 16px; }
|
.row { display: flex; gap: 10px; margin-top: 16px; }
|
||||||
.primary { flex: 1; padding: 14px; font-size: 1rem; border-radius: 3px; border: 0; background: #00e5ff; color: #002; font-weight: 700; }
|
.primary { flex: 1; padding: 14px; font-size: 1rem; border-radius: 3px; border: 0; background: #00e5ff; color: #002; font-weight: 700; cursor: pointer; }
|
||||||
.primary:disabled { background: #333; color: #777; }
|
.primary:disabled { background: #333; color: #777; cursor: not-allowed; }
|
||||||
.ghost { padding: 14px 18px; border-radius: 3px; border: 1px solid #444; background: transparent; color: #ccc; }
|
.ghost { box-sizing: border-box; padding: 14px 18px; border-radius: 3px; border: 1px solid #444; background: transparent; color: #ccc; font-size: .9rem; font-weight: 600; cursor: pointer; }
|
||||||
.err { color: #e88; } .busy { color: #aaa; }
|
.err { color: #e88; } .busy { color: #aaa; }
|
||||||
|
|
||||||
/* #90 Photo strip */
|
/* #90 Photo strip */
|
||||||
|
|||||||
@@ -44,3 +44,15 @@ test('Review UI: View prompt + Create desc gate wiring', () => {
|
|||||||
// Description is not part of the Create-desc gate label list
|
// Description is not part of the Create-desc gate label list
|
||||||
assert.equal(reviewSrc.includes("Create desc needs: description"), false);
|
assert.equal(reviewSrc.includes("Create desc needs: description"), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Review UI: live field commit + unified secondary buttons + moisture default', () => {
|
||||||
|
assert.match(reviewSrc, /scheduleFieldCommit/);
|
||||||
|
assert.match(reviewSrc, /scheduleDescCommit/);
|
||||||
|
assert.match(reviewSrc, /class="btn-sec"/);
|
||||||
|
assert.match(reviewSrc, /applyMoistureDefault/);
|
||||||
|
assert.match(reviewSrc, /void syncSelects\(\)/);
|
||||||
|
// No leftover white unstyled view-prompt class
|
||||||
|
assert.equal(reviewSrc.includes('view-prompt-btn'), false);
|
||||||
|
assert.equal(reviewSrc.includes('create-btn'), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user