fix: dashboard layout spacing and CSS cascade layer bug

Move CSS reset into @layer base so Tailwind utilities aren't overridden.
Fix settings panel: search icon overlap, cramped widget items, spacing
between header card and widget grid using inline margin styles.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ashwin Kumar 2026-03-27 20:10:08 +01:00
parent 7c4ed94975
commit bed0942d12
2 changed files with 57 additions and 42 deletions

View file

@ -21,7 +21,9 @@
--font-family-sans: 'Exo 2', sans-serif;
}
@layer base {
* { box-sizing: border-box; margin: 0; padding: 0; }
}
body {
font-family: 'Exo 2', sans-serif;

View file

@ -322,18 +322,18 @@ export default function AdminHomePage() {
return (
<AdminShell>
<div class="w-full bg-[#F9FAFB] pb-12">
<div class="overflow-hidden rounded-2xl border border-[#E5E7EB] bg-white px-8 pt-10 pb-12 shadow-sm md:px-10 md:pt-11 md:pb-13">
<div class="flex flex-col gap-5 lg:flex-row lg:items-center lg:justify-between">
<div class="max-w-3xl" style="padding-left: 40px; padding-right: 20px;">
<h1 class="text-[28px] font-bold leading-tight text-[#111827]">Dashboard Overview</h1>
<p class="mt-1 text-[14px] text-[#6B7280]">Manage widget layout, visibility, sizing, and dashboard presentation</p>
<div class="w-full">
<div class="rounded-2xl border border-[#E5E7EB] bg-white px-6 py-5 shadow-sm md:px-8" style="margin-bottom: 28px">
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<div>
<h1 class="text-[22px] font-bold leading-tight text-[#111827]">Dashboard Overview</h1>
<p class="mt-0.5 text-[13px] text-[#6B7280]">Manage widget layout, visibility, sizing, and dashboard presentation</p>
</div>
<div class="flex w-full lg:w-auto lg:justify-end" style="margin-right: 20px;">
<div class="flex shrink-0">
<button
type="button"
class="inline-flex h-9 w-full items-center justify-center gap-1.5 whitespace-nowrap rounded-xl bg-[#0D0D2A] px-4 text-[13px] font-semibold leading-none text-white shadow-sm lg:w-auto lg:min-w-[190px]"
class="inline-flex h-9 items-center gap-1.5 whitespace-nowrap rounded-xl bg-[#0D0D2A] px-5 text-[13px] font-semibold leading-none text-white shadow-sm"
onClick={() => {
const next = !settingsOpen();
setSettingsOpen(next);
@ -347,14 +347,13 @@ export default function AdminHomePage() {
</div>
</div>
<div class={settingsOpen() ? 'h-10' : 'h-0'} />
<Show when={settingsOpen()}>
<div class="rounded-2xl border border-[#E5E7EB] bg-white p-8 shadow-sm md:p-9">
<div class="flex flex-wrap items-center justify-between gap-4">
<div class="pl-2 pt-1">
<div class="rounded-2xl border border-[#E5E7EB] bg-white p-6 shadow-sm md:p-7">
{/* Settings header */}
<div class="flex flex-wrap items-start justify-between gap-4">
<div>
<h2 class="text-[15px] font-semibold text-[#111827]">Widget Settings</h2>
<p class="mt-2 text-[13px] text-[#6B7280]">Choose visible widgets and select a grid layout.</p>
<p class="mt-1 text-[13px] text-[#6B7280]">Choose visible widgets and select a grid layout.</p>
</div>
<button
type="button"
@ -365,68 +364,83 @@ export default function AdminHomePage() {
Reset Layout
</button>
</div>
<div class="mt-6 grid gap-5 md:grid-cols-2 xl:grid-cols-4">
<label class="relative">
<Search size={14} class="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-[#FA5014]" />
{/* Filter controls */}
<div class="mt-5 flex flex-wrap gap-3">
<div class="relative min-w-[200px] flex-1">
<Search size={14} class="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-[#FA5014]" style="z-index:1" />
<input
value={search()}
onInput={(event) => setSearch(event.currentTarget.value)}
placeholder="Search widgets"
class="h-11 w-full rounded-lg border border-[#E5E7EB] bg-white pl-9 pr-3 text-sm text-[#111827] outline-none"
class="h-10 w-full rounded-lg border border-[#E5E7EB] bg-white pl-10 pr-3 text-sm text-[#111827] outline-none focus:border-[#FA5014]"
/>
</label>
</div>
<select
class="h-11 rounded-lg border border-[#E5E7EB] bg-white px-3 text-sm text-[#111827]"
class="h-10 rounded-lg border border-[#E5E7EB] bg-white px-3 text-sm text-[#111827]"
value={filterMode()}
onChange={(event) => setFilterMode(event.currentTarget.value as FilterMode)}
>
<option value="all">Filter: All Widgets</option>
<option value="summary">Filter: Summary Widgets</option>
<option value="analytics">Filter: Analytical Widgets</option>
<option value="all">All Widgets</option>
<option value="summary">Summary Only</option>
<option value="analytics">Analytical Only</option>
</select>
<select
class="h-11 rounded-lg border border-[#E5E7EB] bg-white px-3 text-sm text-[#111827]"
class="h-10 rounded-lg border border-[#E5E7EB] bg-white px-3 text-sm text-[#111827]"
value={sortMode()}
onChange={(event) => setSortMode(event.currentTarget.value as SortMode)}
>
<option value="layout">Sort: Layout Order</option>
<option value="name">Sort: Name</option>
<option value="status">Sort: Status</option>
<option value="layout">Layout Order</option>
<option value="name">Name</option>
<option value="status">Status</option>
</select>
<select
class="h-11 rounded-lg border border-[#E5E7EB] bg-white px-3 text-sm text-[#111827]"
class="h-10 rounded-lg border border-[#E5E7EB] bg-white px-3 text-sm text-[#111827]"
value={gridLayout()}
onChange={(event) => setGridLayout(event.currentTarget.value as GridLayoutMode)}
>
<option value="3x4">Grid: 3 x 4</option>
<option value="3x3">Grid: 3 x 3</option>
<option value="3x4">Grid 3 × 4</option>
<option value="3x3">Grid 3 × 3</option>
</select>
</div>
<div class="mt-6 grid gap-5 md:grid-cols-2">
{/* Widget list */}
<div class="mt-5 grid gap-3 md:grid-cols-2">
<For each={ADMIN_DASHBOARD_WIDGETS}>
{(definition) => {
const visible = () => layout().visibility[definition.widgetKey] !== false;
const meta = WIDGET_META[definition.widgetKey];
const state = meta?.state || 'empty';
return (
<div class="rounded-2xl border border-[#E5E7EB] bg-[#F9FAFB] px-4 py-5">
<div class="flex items-center justify-between gap-3">
<div>
<p class="text-sm font-semibold text-[#111827]">{definition.title}</p>
<p class="mt-0.5 text-[11px] font-semibold uppercase tracking-wide text-[#9CA3AF]">{definition.moduleKey}</p>
<div class="flex items-center justify-between gap-4 rounded-xl border border-[#E5E7EB] bg-[#F9FAFB] px-4 py-4">
<div class="flex items-center gap-3 min-w-0">
<div class="shrink-0 text-[#FA5014]">
{iconForWidget(definition.widgetKey)}
</div>
<div class="min-w-0">
<p class="text-sm font-semibold text-[#111827]">{definition.title}</p>
<p class="mt-0.5 text-[11px] font-medium uppercase tracking-wide text-[#9CA3AF]">{definition.moduleKey}</p>
</div>
</div>
<div class="flex shrink-0 items-center gap-2">
<span class={`hidden rounded-full border px-2 py-0.5 text-[10px] font-semibold sm:inline-flex ${badgeClass(state)}`}>
{meta?.statusLabel || 'No Data'}
</span>
<button
type="button"
class={`inline-flex h-8 items-center gap-1 rounded-md border px-3 text-xs font-semibold ${
class={`inline-flex h-8 items-center gap-1.5 rounded-md border px-3 text-xs font-semibold ${
visible()
? 'border-[#FDBA8C] bg-[#FFF1EB] text-[#FA5014]'
: 'border-[#E5E7EB] bg-white text-[#6B7280]'
}`}
onClick={() => setWidgetVisibility(definition.widgetKey, !visible())}
>
{visible() ? <Eye size={13} class="text-[#FA5014]" /> : <EyeOff size={13} class="text-[#FA5014]" />} {visible() ? 'Visible' : 'Hidden'}
{visible() ? <Eye size={12} /> : <EyeOff size={12} />}
{visible() ? 'Visible' : 'Hidden'}
</button>
</div>
</div>
@ -434,15 +448,14 @@ export default function AdminHomePage() {
}}
</For>
</div>
<Show when={isAutoSaving() || autoSaveNotice()}>
<p class="mt-4 text-xs text-[#6B7280]">{isAutoSaving() ? 'Saving layout...' : autoSaveNotice()}</p>
</Show>
</div>
</Show>
<div class={settingsOpen() ? 'h-8' : 'h-12'} />
<div class="grid grid-cols-1 gap-7 xl:grid-cols-12">
<div class="grid grid-cols-1 gap-5 xl:grid-cols-12" style="margin-top: 28px">
<For each={orderedWidgets()}>
{(definition) => {
const meta = WIDGET_META[definition.widgetKey];