Add info tooltips to form fields across remotes, settings, and restore pages
Adds ⓘ tooltip icons with contextual help text to technical fields: - remotes.cgi: SSH key, S3 endpoint, GDrive service account/folder ID, base dir, bandwidth limit, rsync options, retention count - settings.cgi: working dir, log retention, include/exclude accounts, lock file, SSH timeout/retries, rsync options - restore.cgi: restore mode, restore strategy Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -567,7 +567,7 @@ sub render_remote_form {
|
||||
|
||||
# Key field
|
||||
print qq{<div id="auth-key-field"$key_hidden>\n};
|
||||
_field($conf, 'REMOTE_KEY', 'SSH Private Key', 'Absolute path');
|
||||
_field($conf, 'REMOTE_KEY', 'SSH Private Key', 'Absolute path', 'Path to the private key file used for passwordless SSH authentication');
|
||||
print qq{</div>\n};
|
||||
|
||||
# Password field
|
||||
@@ -592,7 +592,7 @@ sub render_remote_form {
|
||||
_field($conf, 'S3_ACCESS_KEY_ID', 'Access Key ID', 'Required');
|
||||
_password_field($conf, 'S3_SECRET_ACCESS_KEY', 'Secret Access Key', 'Required');
|
||||
_field($conf, 'S3_REGION', 'Region', 'Default: us-east-1');
|
||||
_field($conf, 'S3_ENDPOINT', 'Custom Endpoint', 'For MinIO, Wasabi, etc.');
|
||||
_field($conf, 'S3_ENDPOINT', 'Custom Endpoint', 'For MinIO, Wasabi, etc.', 'Only needed for S3-compatible services, leave empty for AWS');
|
||||
_field($conf, 'S3_BUCKET', 'Bucket Name', 'Required');
|
||||
print qq{<p class="text-xs text-base-content/60 mt-2">Requires <code>rclone</code> installed on this server.</p>\n};
|
||||
print qq{</div>\n</div>\n};
|
||||
@@ -604,8 +604,8 @@ sub render_remote_form {
|
||||
print qq{<div id="type-gdrive-fields"$gdrive_hidden>\n};
|
||||
print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
|
||||
print qq{<h2 class="card-title text-sm">Google Drive</h2>\n};
|
||||
_field($conf, 'GDRIVE_SERVICE_ACCOUNT_FILE', 'Service Account JSON', 'Absolute path, required');
|
||||
_field($conf, 'GDRIVE_ROOT_FOLDER_ID', 'Root Folder ID', 'Optional');
|
||||
_field($conf, 'GDRIVE_SERVICE_ACCOUNT_FILE', 'Service Account JSON', 'Absolute path, required', 'Google Cloud service account key file for API access');
|
||||
_field($conf, 'GDRIVE_ROOT_FOLDER_ID', 'Root Folder ID', 'Optional', 'Google Drive folder ID to use as the root for backups');
|
||||
print qq{<p class="text-xs text-base-content/60 mt-2">Requires <code>rclone</code> installed on this server.</p>\n};
|
||||
print qq{</div>\n</div>\n};
|
||||
print qq{</div>\n};
|
||||
@@ -613,22 +613,22 @@ sub render_remote_form {
|
||||
# ── Common fields ─────────────────────────────────────────
|
||||
print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
|
||||
print qq{<h2 class="card-title text-sm">Storage Path</h2>\n};
|
||||
_field($conf, 'REMOTE_BASE', 'Remote Base Dir', 'Default: /backups');
|
||||
_field($conf, 'REMOTE_BASE', 'Remote Base Dir', 'Default: /backups', 'Root directory on the remote where all backup snapshots are stored');
|
||||
print qq{</div>\n</div>\n};
|
||||
|
||||
# Transfer
|
||||
print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
|
||||
print qq{<h2 class="card-title text-sm">Transfer Settings</h2>\n};
|
||||
_field($conf, 'BWLIMIT', 'Bandwidth Limit', 'KB/s, 0 = unlimited');
|
||||
_field($conf, 'BWLIMIT', 'Bandwidth Limit', 'KB/s, 0 = unlimited', 'Throttle transfer speed to avoid saturating the network');
|
||||
print qq{<div id="rsync-opts-field"$ssh_hidden>\n};
|
||||
_field($conf, 'RSYNC_EXTRA_OPTS', 'Extra rsync Options', 'SSH only');
|
||||
_field($conf, 'RSYNC_EXTRA_OPTS', 'Extra rsync Options', 'SSH only', 'Additional flags appended to rsync commands, e.g. --compress');
|
||||
print qq{</div>\n};
|
||||
print qq{</div>\n</div>\n};
|
||||
|
||||
# Retention
|
||||
print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
|
||||
print qq{<h2 class="card-title text-sm">Retention</h2>\n};
|
||||
_field($conf, 'RETENTION_COUNT', 'Snapshots to Keep', 'Default: 30');
|
||||
_field($conf, 'RETENTION_COUNT', 'Snapshots to Keep', 'Default: 30', 'Number of backup snapshots to retain per account before pruning old ones');
|
||||
print qq{</div>\n</div>\n};
|
||||
|
||||
# Submit
|
||||
@@ -768,11 +768,12 @@ JS
|
||||
}
|
||||
|
||||
sub _field {
|
||||
my ($conf, $key, $label, $hint) = @_;
|
||||
my ($conf, $key, $label, $hint, $tip) = @_;
|
||||
my $val = GnizaWHM::UI::esc($conf->{$key} // '');
|
||||
my $hint_html = $hint ? qq{ <span class="text-xs text-base-content/60 ml-2">$hint</span>} : '';
|
||||
my $tip_html = $tip ? qq{ <span class="tooltip tooltip-top" data-tip="$tip">ⓘ</span>} : '';
|
||||
print qq{<div class="flex items-center gap-3 mb-2.5">\n};
|
||||
print qq{ <label class="w-44 font-medium text-sm" for="$key">$label</label>\n};
|
||||
print qq{ <label class="w-44 font-medium text-sm whitespace-nowrap" for="$key">$label$tip_html</label>\n};
|
||||
print qq{ <input type="text" class="input input-bordered input-sm w-full max-w-xs" id="$key" name="$key" value="$val">\n};
|
||||
print qq{ $hint_html\n} if $hint;
|
||||
print qq{</div>\n};
|
||||
|
||||
@@ -240,7 +240,7 @@ sub handle_step2 {
|
||||
|
||||
# Restore mode toggle: Full Account vs Selective
|
||||
print qq{<div class="flex items-center gap-3 mb-2.5">\n};
|
||||
print qq{ <label class="w-44 font-medium text-sm">Restore Mode</label>\n};
|
||||
print qq{ <label class="w-44 font-medium text-sm whitespace-nowrap">Restore Mode <span class="tooltip tooltip-top" data-tip="Full Account restores everything; Selective lets you pick specific items like files, databases, or mailboxes">ⓘ</span></label>\n};
|
||||
print qq{ <div class="join inline-flex items-stretch">\n};
|
||||
print qq{ <input type="radio" name="restore_mode" class="join-item btn btn-sm m-0" aria-label="Full Account" value="full" checked onchange="gnizaModeChanged()">\n};
|
||||
print qq{ <input type="radio" name="restore_mode" class="join-item btn btn-sm m-0" aria-label="Selective" value="selective" onchange="gnizaModeChanged()">\n};
|
||||
@@ -249,7 +249,7 @@ sub handle_step2 {
|
||||
|
||||
# Restore strategy (visible only for Full Account mode)
|
||||
print qq{<div id="strategy-panel" class="flex items-center gap-3 mb-2.5">\n};
|
||||
print qq{ <label class="w-44 font-medium text-sm">Restore Strategy</label>\n};
|
||||
print qq{ <label class="w-44 font-medium text-sm whitespace-nowrap">Restore Strategy <span class="tooltip tooltip-top" data-tip="Overwrite merges into the existing account; Terminate deletes the account first and re-creates it from the backup">ⓘ</span></label>\n};
|
||||
print qq{ <div class="join inline-flex items-stretch">\n};
|
||||
print qq{ <input type="radio" name="strategy" class="join-item btn btn-sm m-0" aria-label="Overwrite (merge)" value="merge" checked>\n};
|
||||
print qq{ <input type="radio" name="strategy" class="join-item btn btn-sm m-0" aria-label="Terminate & re-create" value="terminate">\n};
|
||||
|
||||
@@ -123,12 +123,13 @@ if (@errors && $method eq 'POST') {
|
||||
|
||||
# Helper to output a text field row
|
||||
sub field_text {
|
||||
my ($key, $label, $hint, $extra) = @_;
|
||||
my ($key, $label, $hint, $extra, $tip) = @_;
|
||||
$extra //= '';
|
||||
my $val = GnizaWHM::UI::esc($conf->{$key} // '');
|
||||
my $hint_html = $hint ? qq{ <span class="text-xs text-base-content/60 ml-2">$hint</span>} : '';
|
||||
my $tip_html = $tip ? qq{ <span class="tooltip tooltip-top" data-tip="$tip">ⓘ</span>} : '';
|
||||
print qq{<div class="flex items-center gap-3 mb-2.5">\n};
|
||||
print qq{ <label class="w-44 font-medium text-sm" for="$key">$label</label>\n};
|
||||
print qq{ <label class="w-44 font-medium text-sm whitespace-nowrap" for="$key">$label$tip_html</label>\n};
|
||||
print qq{ <input type="text" class="input input-bordered input-sm w-full max-w-xs" id="$key" name="$key" value="$val" $extra>\n};
|
||||
print qq{ $hint_html\n} if $hint;
|
||||
print qq{</div>\n};
|
||||
@@ -158,7 +159,7 @@ print GnizaWHM::UI::csrf_hidden_field();
|
||||
# Section: Local Settings
|
||||
print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
|
||||
print qq{<h2 class="card-title text-sm">Local Settings</h2>\n};
|
||||
field_text('TEMP_DIR', 'Working Directory', 'Default: /usr/local/gniza/workdir');
|
||||
field_text('TEMP_DIR', 'Working Directory', 'Default: /usr/local/gniza/workdir', '', 'Temporary directory used for pkgacct output before transfer');
|
||||
print qq{</div>\n</div>\n};
|
||||
|
||||
# Section: Account Filtering
|
||||
@@ -167,11 +168,11 @@ print qq{<h2 class="card-title text-sm">Account Filtering</h2>\n};
|
||||
my $inc_val = GnizaWHM::UI::esc($conf->{INCLUDE_ACCOUNTS} // '');
|
||||
my $exc_val = GnizaWHM::UI::esc($conf->{EXCLUDE_ACCOUNTS} // '');
|
||||
print qq{<div class="flex items-center gap-3 mb-2.5">\n};
|
||||
print qq{ <label class="w-44 font-medium text-sm" for="INCLUDE_ACCOUNTS">Include Accounts</label>\n};
|
||||
print qq{ <label class="w-44 font-medium text-sm whitespace-nowrap" for="INCLUDE_ACCOUNTS">Include Accounts <span class="tooltip tooltip-top" data-tip="Only back up these accounts. Leave empty to back up all accounts.">ⓘ</span></label>\n};
|
||||
print qq{ <textarea class="textarea textarea-bordered textarea-sm w-full max-w-xs" id="INCLUDE_ACCOUNTS" name="INCLUDE_ACCOUNTS" placeholder="Comma-separated, empty = all">$inc_val</textarea>\n};
|
||||
print qq{</div>\n};
|
||||
print qq{<div class="flex items-center gap-3 mb-2.5">\n};
|
||||
print qq{ <label class="w-44 font-medium text-sm" for="EXCLUDE_ACCOUNTS">Exclude Accounts</label>\n};
|
||||
print qq{ <label class="w-44 font-medium text-sm whitespace-nowrap" for="EXCLUDE_ACCOUNTS">Exclude Accounts <span class="tooltip tooltip-top" data-tip="These accounts will never be backed up">ⓘ</span></label>\n};
|
||||
print qq{ <textarea class="textarea textarea-bordered textarea-sm w-full max-w-xs" id="EXCLUDE_ACCOUNTS" name="EXCLUDE_ACCOUNTS" placeholder="Comma-separated">$exc_val</textarea>\n};
|
||||
print qq{</div>\n};
|
||||
|
||||
@@ -188,7 +189,7 @@ print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div
|
||||
print qq{<h2 class="card-title text-sm">Logging</h2>\n};
|
||||
field_text('LOG_DIR', 'Log Directory', 'Default: /var/log/gniza');
|
||||
field_select('LOG_LEVEL', 'Log Level', ['debug', 'info', 'warn', 'error']);
|
||||
field_text('LOG_RETAIN', 'Log Retention (days)', 'Default: 90');
|
||||
field_text('LOG_RETAIN', 'Log Retention (days)', 'Default: 90', '', 'Log files older than this are automatically deleted');
|
||||
print qq{</div>\n</div>\n};
|
||||
|
||||
# Section: Notifications
|
||||
@@ -225,10 +226,10 @@ print qq{</div>\n</div>\n};
|
||||
# Section: Advanced
|
||||
print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
|
||||
print qq{<h2 class="card-title text-sm">Advanced</h2>\n};
|
||||
field_text('LOCK_FILE', 'Lock File', 'Default: /var/run/gniza.lock');
|
||||
field_text('SSH_TIMEOUT', 'SSH Timeout (seconds)', 'Default: 30');
|
||||
field_text('SSH_RETRIES', 'SSH Retries', 'Default: 3');
|
||||
field_text('RSYNC_EXTRA_OPTS', 'Extra rsync Options', 'Additional flags for rsync');
|
||||
field_text('LOCK_FILE', 'Lock File', 'Default: /var/run/gniza.lock', '', 'Prevents multiple backup processes from running simultaneously');
|
||||
field_text('SSH_TIMEOUT', 'SSH Timeout (seconds)', 'Default: 30', '', 'How long to wait for an SSH connection before giving up');
|
||||
field_text('SSH_RETRIES', 'SSH Retries', 'Default: 3', '', 'Number of rsync retry attempts with exponential backoff on failure');
|
||||
field_text('RSYNC_EXTRA_OPTS', 'Extra rsync Options', 'Additional flags for rsync', '', 'Global extra flags appended to all rsync commands, e.g. --compress');
|
||||
print qq{</div>\n</div>\n};
|
||||
|
||||
# Submit
|
||||
|
||||
Reference in New Issue
Block a user