Add SVG icons to dashboard stat cards

Each of the 6 stat cards now has a contextual SVG icon in the top-right
corner: users, shield-check, upload, server, clock, and file-text.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
shuki
2026-03-05 18:02:13 +02:00
parent 0fb640d585
commit 42ee83f433
3 changed files with 37 additions and 14 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +1,2 @@
<!-- Tailwind/DaisyUI class safelist for gniza WHM plugin -->
<div class="alert alert-error alert-info alert-success alert-warning badge badge-error badge-sm badge-success badge-warning bg-base-100 bg-base-200 bg-neutral bg-primary/10 border border-base-300 border-base-content/5 breadcrumbs btn btn-error btn-ghost btn-info btn-primary btn-secondary btn-sm btn-xs card card-body card-title checkbox checkbox-sm collapse collapse-arrow collapse-content collapse-title cursor-pointer flex flex-1 flex-col flex-wrap font-bold font-medium font-mono font-semibold gap-1 gap-2 gap-3 hidden inline input input-bordered input-sm items-center items-start mx-auto join join-item link list-disc loading loading-spinner loading-xs max-h-48 max-w-2xl max-w-xs mb-1 mb-2.5 mb-3 mb-4 mb-5 mb-6 ml-2 modal modal-action modal-backdrop modal-box mt-2 mt-3 mt-4 mt-5 my-2 my-4 overflow-x-auto overflow-y-auto p-3 p-4 pt-1 pt-2 pl-5 px-4 py-1 py-3 py-4 radio radio-sm rounded-box rounded-lg select select-bordered select-sm shadow-sm steps tab tab-content table hover tabs tabs-box tabs-lg tab-active text-center text-error text-lg textarea textarea-bordered textarea-sm text-base-content/60 text-neutral-content text-sm text-xl text-xs toggle toggle-sm toggle-success w-11/12 w-44 w-full whitespace-pre-wrap font-sans text-[1.6rem] text-warning badge-info badge-neutral btn-active leading-relaxed inline-flex items-stretch w-fit bg-[#fafafa] px-5 max-h-[360px] m-0 no-underline bg-white p-2.5 animate-pulse badge-outline btn-warning btn-circle mt-1 h-40 tooltip tooltip-top w-52 whitespace-nowrap navbar navbar-start navbar-end menu menu-horizontal h-12 w-auto text-3xl leading-none text-secondary active stat stat-title stat-value stat-desc stat-figure grid-cols-3 grid-cols-2 sm:grid-cols-3 badge-lg grid gap-4 lg:hidden lg:flex dropdown dropdown-content dropdown-end h-5 w-5 p-2"></div>
<div class="alert alert-error alert-info alert-success alert-warning badge badge-error badge-sm badge-success badge-warning bg-base-100 bg-base-200 bg-neutral bg-primary/10 border border-base-300 border-base-content/5 breadcrumbs btn btn-error btn-ghost btn-info btn-primary btn-secondary btn-sm btn-xs card card-body card-title checkbox checkbox-sm collapse collapse-arrow collapse-content collapse-title cursor-pointer flex flex-1 flex-col flex-wrap font-bold font-medium font-mono font-semibold gap-1 gap-2 gap-3 hidden inline input input-bordered input-sm items-center items-start mx-auto join join-item link list-disc loading loading-spinner loading-xs max-h-48 max-w-2xl max-w-xs mb-1 mb-2.5 mb-3 mb-4 mb-5 mb-6 ml-2 modal modal-action modal-backdrop modal-box mt-2 mt-3 mt-4 mt-5 my-2 my-4 overflow-x-auto overflow-y-auto p-3 p-4 pt-1 pt-2 pl-5 px-4 py-1 py-3 py-4 radio radio-sm rounded-box rounded-lg select select-bordered select-sm shadow-sm steps tab tab-content table hover tabs tabs-box tabs-lg tab-active text-center text-error text-lg textarea textarea-bordered textarea-sm text-base-content/60 text-neutral-content text-sm text-xl text-xs toggle toggle-sm toggle-success w-11/12 w-44 w-full whitespace-pre-wrap font-sans text-[1.6rem] text-warning badge-info badge-neutral btn-active leading-relaxed inline-flex items-stretch w-fit bg-[#fafafa] px-5 max-h-[360px] m-0 no-underline bg-white p-2.5 animate-pulse badge-outline btn-warning btn-circle mt-1 h-40 tooltip tooltip-top w-52 whitespace-nowrap navbar navbar-start navbar-end menu menu-horizontal h-12 w-auto text-3xl leading-none text-secondary active stat stat-title stat-value stat-desc stat-figure grid-cols-3 grid-cols-2 sm:grid-cols-3 badge-lg grid gap-4 lg:hidden lg:flex dropdown dropdown-content dropdown-end h-5 w-5 p-2 justify-between text-primary/40 text-secondary/40 text-base-content/20"></div>

View File

@@ -96,7 +96,7 @@ print GnizaWHM::UI::render_flash();
}
}
# Updated timestamp + refresh button
# Updated timestamp + refresh button + setup wizard
print qq{<div class="flex items-center gap-3 mb-4">\n};
if ($has_stats && $updated) {
my $esc_updated = GnizaWHM::UI::esc($updated);
@@ -109,16 +109,29 @@ print GnizaWHM::UI::render_flash();
print qq{ } . GnizaWHM::UI::csrf_hidden_field() . qq{\n};
print qq{ <button type="submit" class="btn btn-secondary btn-sm">Refresh Stats</button>\n};
print qq{ </form>\n};
print qq{ <div class="flex-1"></div>\n};
print qq{ <button type="button" class="btn btn-primary btn-sm" onclick="location.href='setup.cgi'">Run Setup Wizard</button>\n};
print qq{</div>\n};
# 6 stat cards in responsive grid
print qq{<div class="grid grid-cols-2 sm:grid-cols-3 gap-4 mb-6">\n};
# SVG icons for stat cards (24x24, stroke-based)
my $icon_accounts = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>';
my $icon_backed_up = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="m9 12 2 2 4-4"/></svg>';
my $icon_snapshots = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>';
my $icon_remotes = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="20" height="8" rx="2" ry="2"/><rect x="2" y="14" width="20" height="8" rx="2" ry="2"/><line x1="6" y1="6" x2="6.01" y2="6"/><line x1="6" y1="18" x2="6.01" y2="18"/></svg>';
my $icon_schedules = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>';
my $icon_last_backup = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>';
# Card 1: cPanel Accounts
my $acct_count = scalar @cpanel_accounts;
print qq{ <div class="card bg-white shadow-sm border border-base-300">\n};
print qq{ <div class="card-body p-4">\n};
print qq{ <div class="flex items-center justify-between">\n};
print qq{ <div class="text-sm text-base-content/60">cPanel Accounts</div>\n};
print qq{ <div class="text-primary/40">$icon_accounts</div>\n};
print qq{ </div>\n};
print qq{ <div class="text-xl font-bold text-primary">$acct_count</div>\n};
print qq{ <div class="text-xs text-base-content/60">on this server</div>\n};
print qq{ </div>\n};
@@ -127,7 +140,10 @@ print GnizaWHM::UI::render_flash();
# Card 2: Backed Up Accounts
print qq{ <div class="card bg-white shadow-sm border border-base-300">\n};
print qq{ <div class="card-body p-4">\n};
print qq{ <div class="flex items-center justify-between">\n};
print qq{ <div class="text-sm text-base-content/60">Backed Up Accounts</div>\n};
print qq{ <div class="text-secondary/40">$icon_backed_up</div>\n};
print qq{ </div>\n};
print qq{ <div class="text-xl font-bold text-secondary">$backed_up</div>\n};
my $remote_count_desc = scalar @remotes;
print qq{ <div class="text-xs text-base-content/60">across $remote_count_desc remote(s)</div>\n};
@@ -137,7 +153,10 @@ print GnizaWHM::UI::render_flash();
# Card 3: Total Snapshots
print qq{ <div class="card bg-white shadow-sm border border-base-300">\n};
print qq{ <div class="card-body p-4">\n};
print qq{ <div class="flex items-center justify-between">\n};
print qq{ <div class="text-sm text-base-content/60">Total Snapshots</div>\n};
print qq{ <div class="text-base-content/20">$icon_snapshots</div>\n};
print qq{ </div>\n};
print qq{ <div class="text-xl font-bold">$total_snaps</div>\n};
print qq{ <div class="text-xs text-base-content/60">across all remotes</div>\n};
print qq{ </div>\n};
@@ -147,7 +166,10 @@ print GnizaWHM::UI::render_flash();
my $rem_count = scalar @remotes;
print qq{ <div class="card bg-white shadow-sm border border-base-300">\n};
print qq{ <div class="card-body p-4">\n};
print qq{ <div class="flex items-center justify-between">\n};
print qq{ <div class="text-sm text-base-content/60">Remotes</div>\n};
print qq{ <div class="text-primary/40">$icon_remotes</div>\n};
print qq{ </div>\n};
print qq{ <div class="text-xl font-bold text-primary">$rem_count</div>\n};
print qq{ <div class="text-xs text-base-content/60">configured destinations</div>\n};
print qq{ </div>\n};
@@ -156,7 +178,10 @@ print GnizaWHM::UI::render_flash();
# Card 5: Schedules
print qq{ <div class="card bg-white shadow-sm border border-base-300">\n};
print qq{ <div class="card-body p-4">\n};
print qq{ <div class="flex items-center justify-between">\n};
print qq{ <div class="text-sm text-base-content/60">Schedules</div>\n};
print qq{ <div class="text-secondary/40">$icon_schedules</div>\n};
print qq{ </div>\n};
print qq{ <div class="text-xl font-bold text-secondary">$sched_count</div>\n};
print qq{ <div class="text-xs text-base-content/60">active cron jobs</div>\n};
print qq{ </div>\n};
@@ -176,7 +201,10 @@ print GnizaWHM::UI::render_flash();
}
print qq{ <div class="card bg-white shadow-sm border border-base-300">\n};
print qq{ <div class="card-body p-4">\n};
print qq{ <div class="flex items-center justify-between">\n};
print qq{ <div class="text-sm text-base-content/60">Last Backup</div>\n};
print qq{ <div class="text-base-content/20">$icon_last_backup</div>\n};
print qq{ </div>\n};
print qq{ <div class="mt-1 mb-1"><span class="badge $badge_class">$badge_text</span></div>\n};
if ($last_log) {
my $esc_log = GnizaWHM::UI::esc($last_log);
@@ -201,11 +229,6 @@ print qq{ </div>\n};
print qq{</div>\n};
print qq{</div>\n</div>\n};
# Quick links
print qq{<div class="flex gap-3 mb-5">\n};
print qq{ <button type="button" class="btn btn-primary btn-sm" onclick="location.href='setup.cgi'">Run Setup Wizard</button>\n};
print qq{</div>\n};
# Remote destinations
my @remotes = GnizaWHM::UI::list_remotes();
print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};