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:
parent
7c4ed94975
commit
bed0942d12
2 changed files with 57 additions and 42 deletions
|
|
@ -21,7 +21,9 @@
|
|||
--font-family-sans: 'Exo 2', sans-serif;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
@layer base {
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Exo 2', sans-serif;
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue