Drop the section headings — the step name is already at the top
Every step's name is in the progress bar. Repeating it as a heading directly beneath was the same word twice with nothing between them: "Step 4 of 8 — Storage" followed immediately by "STORAGE". Eight headings across both branches, all gone. Three of them carried a tooltip worth keeping — what a storage location is, that a backup password cannot be recovered, what a .lpapp is. Those moved to a tooltip beside the step name in the progress bar, which is where the heading actually is now. Steps that never had an explanation show no marker. Also removed the Backup step's "Where is it? Backups live in a repository…" paragraph. The step is called Backup, the first field asks where it lives, and the repository-not-a-file point is what the step tooltip is for. lp-restore-wizard-test asserted the old heading matched the step name; it now asserts there are no headings left anywhere and that the progress bar carries both the name and the explanation. Both browser tests reported "wizard handle missing" when the wizard simply was not on screen — it only renders while setup is incomplete, and a stray .setup_complete from an earlier run hides it. They now say which of the two it is, since the first sends you looking for a JS error that is not there. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
aff9b398da
commit
f736ec6501
@ -28,6 +28,18 @@ class SetupWizard {
|
||||
// must not be asked for an install name, domains or an app list — the
|
||||
// backup already answers all three, and asking would invite someone to
|
||||
// contradict what is about to be restored over their answer.
|
||||
// Tips that used to hang off a section heading. The heading itself is the
|
||||
// progress bar's step name — repeating it as a title directly beneath was
|
||||
// the same word twice with nothing between them — so the explanation moves
|
||||
// to where the name actually is.
|
||||
this.stepTips = {
|
||||
Storage: 'Apps normally live on the system disk. If you have another drive, you can register it here and choose per app where its data goes.',
|
||||
Backups: 'Snapshots of your apps and settings, taken on a schedule. Encrypted, so keep the password somewhere other than this machine \u2014 without it a backup cannot be opened, not even by us.',
|
||||
Import: "Bring apps over from another LibrePortal using .lpapp files made with 'libreportal app export'. Point at a file or a folder of them, somewhere on this machine.",
|
||||
Backup: 'Backups live in a repository \u2014 a folder on a disk, or a remote server. Not a single file, so point us at the repository itself.',
|
||||
Contents: 'What this backup holds, and what it would mean on this machine.',
|
||||
Rebuild: 'What is about to happen, and in what order.'
|
||||
};
|
||||
this.stepNames = ['Start', 'Experience', 'Identity', 'Domains', 'Storage', 'Backups', 'Import', 'Recommended', 'Metrics',
|
||||
'Backup', 'Contents', 'Rebuild'];
|
||||
this.stepIcons = ['\u{1F9ED}', '\u{1F331}', '\u{1FA90}', '\u{1F6F0}\u{FE0F}', '\u{1F4BE}', '\u{1F6DF}', '\u{1F4E6}', '\u{1F6E1}\u{FE0F}', '\u{1F4CA}',
|
||||
@ -358,9 +370,6 @@ class SetupWizard {
|
||||
otherwise there is exactly one answer and the step is skipped. -->
|
||||
<section class="setup-step" data-step="4">
|
||||
<div class="setup-section">
|
||||
<div class="setup-section-title">Storage
|
||||
<span class="setup-tooltip" tabindex="0" data-tip="Apps normally live on the system disk. If you have another drive, you can register it here and choose per app where its data goes.">?</span>
|
||||
</div>
|
||||
<div id="sw-storage-choices"></div>
|
||||
<div id="sw-storage-paths" style="display:none;"></div>
|
||||
<div class="setup-storage-divider" id="sw-storage-drives-head" style="display:none;"><span>Drives</span></div>
|
||||
@ -374,9 +383,6 @@ class SetupWizard {
|
||||
this asks rather than waiting to be found in the Backup page. -->
|
||||
<section class="setup-step" data-step="5">
|
||||
<div class="setup-section">
|
||||
<div class="setup-section-title">Backups
|
||||
<span class="setup-tooltip" tabindex="0" data-tip="Snapshots of your apps and settings, taken on a schedule. Encrypted, so keep the password somewhere other than this machine — without it a backup cannot be opened, not even by us.">?</span>
|
||||
</div>
|
||||
<div id="sw-backup-choices"></div>
|
||||
<div class="setup-storage-divider">Destinations</div>
|
||||
<div id="sw-backup-dests"></div>
|
||||
@ -390,9 +396,6 @@ class SetupWizard {
|
||||
browser (unlike a backup repository — see first-run-restore.md). -->
|
||||
<section class="setup-step" data-step="6">
|
||||
<div class="setup-section">
|
||||
<div class="setup-section-title">Import
|
||||
<span class="setup-tooltip" tabindex="0" data-tip="Bring apps over from another LibrePortal using .lpapp files made with 'libreportal app export'. Point at a file or a folder of them, somewhere on this machine.">?</span>
|
||||
</div>
|
||||
<div class="setup-storage-choice">
|
||||
<span class="setup-storage-choice-label">Folder or file</span>
|
||||
<input type="text" id="sw-import-path" class="form-control" placeholder="/mnt/usb/exports">
|
||||
@ -408,7 +411,6 @@ class SetupWizard {
|
||||
<!-- Step 7: Recommended apps (Traefik + Fail2ban) -->
|
||||
<section class="setup-step" data-step="7">
|
||||
<div class="setup-section">
|
||||
<div class="setup-section-title">Recommended Apps</div>
|
||||
<p class="setup-section-hint">Pre-selected to give you a working install out of the box.</p>
|
||||
<div id="sw-apps-recommended"></div>
|
||||
|
||||
@ -432,7 +434,6 @@ class SetupWizard {
|
||||
is skipped when the user chose Beginner on step 1. -->
|
||||
<section class="setup-step" data-step="8">
|
||||
<div class="setup-section">
|
||||
<div class="setup-section-title">Metrics Apps</div>
|
||||
<p class="setup-section-hint">Optional. Install these to enable per-app "Export metrics to Grafana" later.</p>
|
||||
<div id="sw-apps-metrics"></div>
|
||||
</div>
|
||||
@ -443,11 +444,6 @@ class SetupWizard {
|
||||
<!-- Which backup to rebuild from -->
|
||||
<section class="setup-step" data-step="9">
|
||||
<div class="setup-section">
|
||||
<div class="setup-section-title">Backup</div>
|
||||
<p class="setup-section-hint">
|
||||
Where is it? Backups live in a repository \u2014 a folder on a
|
||||
disk, or a remote server. Not a single file.
|
||||
</p>
|
||||
<div id="sw-rs-fields"></div>
|
||||
|
||||
<div class="setup-field">
|
||||
@ -468,8 +464,6 @@ class SetupWizard {
|
||||
<!-- What is in there -->
|
||||
<section class="setup-step" data-step="10">
|
||||
<div class="setup-section">
|
||||
<div class="setup-section-title">Contents</div>
|
||||
<p class="setup-section-hint">What this backup would bring back.</p>
|
||||
<div id="sw-rs-contents">
|
||||
<p class="setup-section-hint">Go back a step and read the backup first.</p>
|
||||
</div>
|
||||
@ -479,8 +473,6 @@ class SetupWizard {
|
||||
<!-- Confirm -->
|
||||
<section class="setup-step" data-step="11">
|
||||
<div class="setup-section">
|
||||
<div class="setup-section-title">Rebuild</div>
|
||||
<p class="setup-section-hint">What is about to happen, and in what order.</p>
|
||||
<div id="sw-rs-summary"></div>
|
||||
</div>
|
||||
</section>
|
||||
@ -2327,7 +2319,10 @@ class SetupWizard {
|
||||
const icon = this.stepIcons[actual];
|
||||
this.container.querySelector('#sw-progress-fill').style.width = `${pct}%`;
|
||||
this.container.querySelector('#sw-progress-step').innerHTML =
|
||||
`Step ${this.currentStep + 1} of ${this.totalSteps} <span class="setup-progress-sep">—</span> <span class="setup-progress-icon">${icon}</span> <span class="setup-progress-name">${name}</span>`;
|
||||
`Step ${this.currentStep + 1} of ${this.totalSteps} <span class="setup-progress-sep">—</span> <span class="setup-progress-icon">${icon}</span> <span class="setup-progress-name">${name}</span>` +
|
||||
(this.stepTips[name]
|
||||
? ` <span class="setup-tooltip" tabindex="0" data-tip="${this.escapeHtml(this.stepTips[name])}">?</span>`
|
||||
: '');
|
||||
this.container.querySelector('#sw-progress-pct').textContent = `${pct}%`;
|
||||
|
||||
this.container.querySelector('#sw-back').disabled = this.currentStep === 0;
|
||||
|
||||
@ -32,7 +32,16 @@ lp_reachable() {
|
||||
read -r -d '' DRIVE <<'JS'
|
||||
const out = {};
|
||||
const w = window.setupWizard;
|
||||
if (!w) return JSON.stringify({ error: 'wizard handle missing' });
|
||||
if (!w) {
|
||||
// Distinguish "the wizard is broken" from "the wizard is not on screen".
|
||||
// It only renders while setup is incomplete, so a stray .setup_complete —
|
||||
// which a previous test run can leave behind — hides it entirely, and
|
||||
// "wizard handle missing" sends you looking for a JS error that is not
|
||||
// there.
|
||||
return JSON.stringify({ error: typeof window.SetupWizard === 'function'
|
||||
? 'the wizard did not open — setup is already marked complete on this install (remove frontend/data/.setup_complete)'
|
||||
: 'wizard handle missing' });
|
||||
}
|
||||
const fire = (el, ev) => el.dispatchEvent(new Event(ev, { bubbles: true }));
|
||||
const $ = s => document.querySelector(s);
|
||||
const visible = () => w.stepNames.filter((n, i) => w._stepVisible(i));
|
||||
@ -56,10 +65,17 @@ read -r -d '' DRIVE <<'JS'
|
||||
out.startAlwaysShown = out.newSteps[0] === 'Start' && out.restoreSteps[0] === 'Start';
|
||||
|
||||
// The source form offers every backend, and shows only the chosen one.
|
||||
// The progress bar said "Source" while the heading said "Where is your
|
||||
// backup?", which read as two different steps. They have to agree.
|
||||
const sec = document.querySelector('.setup-step[data-step="9"] .setup-section-title');
|
||||
out.titleMatchesStepName = !!sec && sec.textContent.trim() === 'Backup';
|
||||
// The step's name lives in the progress bar. Repeating it as a heading
|
||||
// directly beneath was the same word twice with nothing between them, so
|
||||
// there are no section headings left in the wizard at all — and what those
|
||||
// headings explained moved to a tooltip beside the name, which is where the
|
||||
// name is.
|
||||
out.noSectionHeadingsAnywhere = document.querySelectorAll('.setup-section-title').length;
|
||||
w.showStep(1);
|
||||
const bar = document.querySelector('#sw-progress-step');
|
||||
out.progressShowsTheStepName =
|
||||
(bar.querySelector('.setup-progress-name') || {}).textContent === 'Backup';
|
||||
out.progressCarriesTheTip = !!bar.querySelector('.setup-tooltip');
|
||||
// Fields laid out like the rest of the wizard: label with a tooltip, and an
|
||||
// icon beside the input — not the label-left rows the Storage step uses.
|
||||
out.fieldsHaveIcons = document.querySelectorAll('#sw-rs-fields .setup-field-icon').length > 0;
|
||||
@ -320,7 +336,9 @@ chk "restore shows no install steps" "$(g .restoreLeaksNewStep)" false
|
||||
chk "new shows no restore steps" "$(g .newLeaksRestoreStep)" false
|
||||
chk "restore step set" "$(g '.restoreSteps | join(",")')" "Start,Backup,Contents,Rebuild"
|
||||
|
||||
chk "step name matches its title" "$(g .titleMatchesStepName)" true
|
||||
chk "no section headings anywhere" "$(g .noSectionHeadingsAnywhere)" 0
|
||||
chk "the progress bar names the step" "$(g .progressShowsTheStepName)" true
|
||||
chk "and carries its explanation" "$(g .progressCarriesTheTip)" true
|
||||
|
||||
echo "the backup source form"
|
||||
chk "every backend offered" "$(g '.kinds | join(",")')" "local,sftp,rest,s3,b2"
|
||||
|
||||
@ -43,7 +43,16 @@ lp_reachable() {
|
||||
read -r -d '' DRIVE <<'JS'
|
||||
const out = {};
|
||||
const w = window.setupWizard;
|
||||
if (!w) return JSON.stringify({ error: 'wizard handle missing' });
|
||||
if (!w) {
|
||||
// Distinguish "the wizard is broken" from "the wizard is not on screen".
|
||||
// It only renders while setup is incomplete, so a stray .setup_complete —
|
||||
// which a previous test run can leave behind — hides it entirely, and
|
||||
// "wizard handle missing" sends you looking for a JS error that is not
|
||||
// there.
|
||||
return JSON.stringify({ error: typeof window.SetupWizard === 'function'
|
||||
? 'the wizard did not open — setup is already marked complete on this install (remove frontend/data/.setup_complete)'
|
||||
: 'wizard handle missing' });
|
||||
}
|
||||
const fire = (el, ev) => el.dispatchEvent(new Event(ev, { bubbles: true }));
|
||||
// Looked up by name, never hardcoded. Inserting the Start step moved Storage
|
||||
// from 3 to 4, and a test pinned to the old number silently began validating
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user