diff --git a/apps/publisher-dashboard/package.json b/apps/publisher-dashboard/package.json index 113ae9f..5e73db1 100644 --- a/apps/publisher-dashboard/package.json +++ b/apps/publisher-dashboard/package.json @@ -19,7 +19,7 @@ }, "devDependencies": { "@internationalized/date": "^3.10.1", - "@lucide/svelte": "^0.562.0", + "@lucide/svelte": "^0.561.0", "@macalinao/tsconfig": "catalog:", "@sveltejs/adapter-static": "^3.0.8", "@sveltejs/kit": "^2.21.1", diff --git a/apps/publisher-dashboard/src/app.css b/apps/publisher-dashboard/src/app.css index ebd9704..48f3f72 100644 --- a/apps/publisher-dashboard/src/app.css +++ b/apps/publisher-dashboard/src/app.css @@ -1,83 +1,170 @@ @import "tailwindcss"; - @import "tw-animate-css"; +/* Geist Sans - Modern, clean typeface */ +@font-face { + font-family: "Geist"; + src: url("https://cdn.jsdelivr.net/npm/geist@1.3.1/dist/fonts/geist-sans/Geist-Regular.woff2") + format("woff2"); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "Geist"; + src: url("https://cdn.jsdelivr.net/npm/geist@1.3.1/dist/fonts/geist-sans/Geist-Medium.woff2") + format("woff2"); + font-weight: 500; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "Geist"; + src: url("https://cdn.jsdelivr.net/npm/geist@1.3.1/dist/fonts/geist-sans/Geist-SemiBold.woff2") + format("woff2"); + font-weight: 600; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "Geist"; + src: url("https://cdn.jsdelivr.net/npm/geist@1.3.1/dist/fonts/geist-sans/Geist-Bold.woff2") + format("woff2"); + font-weight: 700; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "Geist Mono"; + src: url("https://cdn.jsdelivr.net/npm/geist@1.3.1/dist/fonts/geist-mono/GeistMono-Regular.woff2") + format("woff2"); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "Geist Mono"; + src: url("https://cdn.jsdelivr.net/npm/geist@1.3.1/dist/fonts/geist-mono/GeistMono-Medium.woff2") + format("woff2"); + font-weight: 500; + font-style: normal; + font-display: swap; +} + @custom-variant dark (&:is(.dark *)); :root { - --radius: 0.625rem; - --background: oklch(1 0 0); - --foreground: oklch(0.141 0.005 285.823); + --radius: 0.5rem; + + /* Refined zinc palette with subtle warmth */ + --background: oklch(0.985 0.002 280); + --foreground: oklch(0.145 0.005 285); + --card: oklch(1 0 0); - --card-foreground: oklch(0.141 0.005 285.823); + --card-foreground: oklch(0.145 0.005 285); + --popover: oklch(1 0 0); - --popover-foreground: oklch(0.141 0.005 285.823); - --primary: oklch(0.21 0.006 285.885); + --popover-foreground: oklch(0.145 0.005 285); + + --primary: oklch(0.205 0.006 285); --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.967 0.001 286.375); - --secondary-foreground: oklch(0.21 0.006 285.885); - --muted: oklch(0.967 0.001 286.375); - --muted-foreground: oklch(0.552 0.016 285.938); - --accent: oklch(0.967 0.001 286.375); - --accent-foreground: oklch(0.21 0.006 285.885); + + --secondary: oklch(0.96 0.002 280); + --secondary-foreground: oklch(0.205 0.006 285); + + --muted: oklch(0.96 0.002 280); + --muted-foreground: oklch(0.48 0.012 280); + + --accent: oklch(0.96 0.002 280); + --accent-foreground: oklch(0.205 0.006 285); + --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.92 0.004 286.32); - --input: oklch(0.92 0.004 286.32); - --ring: oklch(0.705 0.015 286.067); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.141 0.005 285.823); - --sidebar-primary: oklch(0.21 0.006 285.885); + + --border: oklch(0.91 0.003 280); + --input: oklch(0.91 0.003 280); + --ring: oklch(0.65 0.015 280); + + /* Accent colors for data viz */ + --success: oklch(0.65 0.19 145); + --warning: oklch(0.75 0.18 65); + + --chart-1: oklch(0.55 0.2 250); + --chart-2: oklch(0.6 0.15 175); + --chart-3: oklch(0.5 0.12 220); + --chart-4: oklch(0.7 0.16 85); + --chart-5: oklch(0.65 0.18 35); + + --sidebar: oklch(0.98 0.001 280); + --sidebar-foreground: oklch(0.145 0.005 285); + --sidebar-primary: oklch(0.205 0.006 285); --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.967 0.001 286.375); - --sidebar-accent-foreground: oklch(0.21 0.006 285.885); - --sidebar-border: oklch(0.92 0.004 286.32); - --sidebar-ring: oklch(0.705 0.015 286.067); + --sidebar-accent: oklch(0.94 0.002 280); + --sidebar-accent-foreground: oklch(0.205 0.006 285); + --sidebar-border: oklch(0.91 0.003 280); + --sidebar-ring: oklch(0.65 0.015 280); } .dark { - --background: oklch(0.141 0.005 285.823); - --foreground: oklch(0.985 0 0); - --card: oklch(0.21 0.006 285.885); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.21 0.006 285.885); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.92 0.004 286.32); - --primary-foreground: oklch(0.21 0.006 285.885); - --secondary: oklch(0.274 0.006 286.033); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.274 0.006 286.033); - --muted-foreground: oklch(0.705 0.015 286.067); - --accent: oklch(0.274 0.006 286.033); - --accent-foreground: oklch(0.985 0 0); - --destructive: oklch(0.704 0.191 22.216); - --border: oklch(1 0 0 / 10%); - --input: oklch(1 0 0 / 15%); - --ring: oklch(0.552 0.016 285.938); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.21 0.006 285.885); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.274 0.006 286.033); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.552 0.016 285.938); + --background: oklch(0.12 0.005 280); + --foreground: oklch(0.96 0 0); + + --card: oklch(0.16 0.005 280); + --card-foreground: oklch(0.96 0 0); + + --popover: oklch(0.16 0.005 280); + --popover-foreground: oklch(0.96 0 0); + + --primary: oklch(0.92 0.003 280); + --primary-foreground: oklch(0.12 0.005 280); + + --secondary: oklch(0.22 0.005 280); + --secondary-foreground: oklch(0.96 0 0); + + --muted: oklch(0.22 0.005 280); + --muted-foreground: oklch(0.6 0.01 280); + + --accent: oklch(0.22 0.005 280); + --accent-foreground: oklch(0.96 0 0); + + --destructive: oklch(0.65 0.2 25); + + --border: oklch(0.26 0.005 280); + --input: oklch(0.26 0.005 280); + --ring: oklch(0.5 0.01 280); + + --success: oklch(0.7 0.2 145); + --warning: oklch(0.8 0.18 65); + + --chart-1: oklch(0.6 0.22 250); + --chart-2: oklch(0.65 0.17 175); + --chart-3: oklch(0.7 0.18 85); + --chart-4: oklch(0.6 0.2 310); + --chart-5: oklch(0.65 0.22 25); + + --sidebar: oklch(0.14 0.005 280); + --sidebar-foreground: oklch(0.96 0 0); + --sidebar-primary: oklch(0.6 0.22 250); + --sidebar-primary-foreground: oklch(0.96 0 0); + --sidebar-accent: oklch(0.22 0.005 280); + --sidebar-accent-foreground: oklch(0.96 0 0); + --sidebar-border: oklch(0.26 0.005 280); + --sidebar-ring: oklch(0.5 0.01 280); } @theme inline { - --radius-sm: calc(var(--radius) - 4px); - --radius-md: calc(var(--radius) - 2px); - --radius-lg: var(--radius); - --radius-xl: calc(var(--radius) + 4px); + --font-sans: "Geist", ui-sans-serif, system-ui, sans-serif; + --font-mono: "Geist Mono", ui-monospace, monospace; + + --radius-sm: calc(var(--radius) - 2px); + --radius-md: var(--radius); + --radius-lg: calc(var(--radius) + 2px); + --radius-xl: calc(var(--radius) + 6px); + --color-background: var(--background); --color-foreground: var(--foreground); --color-card: var(--card); @@ -96,6 +183,8 @@ --color-border: var(--border); --color-input: var(--input); --color-ring: var(--ring); + --color-success: var(--success); + --color-warning: var(--warning); --color-chart-1: var(--chart-1); --color-chart-2: var(--chart-2); --color-chart-3: var(--chart-3); @@ -113,13 +202,57 @@ @layer base { * { - @apply border-border outline-ring/50; + @apply border-border; } + + html { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; + } + body { - @apply bg-background text-foreground; + @apply bg-background font-sans text-foreground; + font-feature-settings: + "rlig" 1, + "calt" 1; } + button, [role="button"] { @apply cursor-pointer; } -} \ No newline at end of file + + /* Refined focus styles */ + :focus-visible { + @apply outline-none ring-2 ring-ring/40 ring-offset-2 ring-offset-background; + } + + /* Smooth scrolling */ + @media (prefers-reduced-motion: no-preference) { + html { + scroll-behavior: smooth; + } + } +} + +/* Utility classes for consistent shadows */ +@layer utilities { + .shadow-card { + box-shadow: + 0 1px 2px 0 oklch(0 0 0 / 0.03), + 0 1px 3px 0 oklch(0 0 0 / 0.04); + } + + .shadow-card-hover { + box-shadow: + 0 2px 4px 0 oklch(0 0 0 / 0.04), + 0 3px 6px 0 oklch(0 0 0 / 0.05); + } + + .shadow-elevated { + box-shadow: + 0 4px 6px -1px oklch(0 0 0 / 0.05), + 0 2px 4px -2px oklch(0 0 0 / 0.05); + } +} diff --git a/apps/publisher-dashboard/src/lib/components/dashboard/frequent-filters.svelte b/apps/publisher-dashboard/src/lib/components/dashboard/frequent-filters.svelte index 8ec9121..51e780c 100644 --- a/apps/publisher-dashboard/src/lib/components/dashboard/frequent-filters.svelte +++ b/apps/publisher-dashboard/src/lib/components/dashboard/frequent-filters.svelte @@ -1,5 +1,4 @@ - - - Frequent filters - +
+
+

Saved Filters

+
- + + +
+ +
+
diff --git a/apps/publisher-dashboard/src/lib/components/dashboard/metric-card.svelte b/apps/publisher-dashboard/src/lib/components/dashboard/metric-card.svelte index e4d1b3d..edec382 100644 --- a/apps/publisher-dashboard/src/lib/components/dashboard/metric-card.svelte +++ b/apps/publisher-dashboard/src/lib/components/dashboard/metric-card.svelte @@ -1,5 +1,4 @@ - - -
-
-

{label}

-
- {value} - - {isPositive ? "+" : ""}{change.toFixed(2)}% - -
-
+
+
+
+

{label}

- {#if sparklineData.length > 0} -
- - +
+ {value} + + + {#if isPositive} + + {:else} + + {/if} -
- {/if} + {Math.abs(change).toFixed(2)}% + +
- - + + {#if sparklineData.length > 0} +
+ + + + + + + + + + + + + + + + + + +
+ {/if} +
+
diff --git a/apps/publisher-dashboard/src/lib/components/dashboard/peak-traffic-chart.svelte b/apps/publisher-dashboard/src/lib/components/dashboard/peak-traffic-chart.svelte index 30769d8..ab172de 100644 --- a/apps/publisher-dashboard/src/lib/components/dashboard/peak-traffic-chart.svelte +++ b/apps/publisher-dashboard/src/lib/components/dashboard/peak-traffic-chart.svelte @@ -1,5 +1,4 @@ - - - Peak Traffic Hours - +
+
+

Peak Traffic Hours

+
- -
+
+
-
+
{#each hours as hour} - {hour} +
+ {hour} +
{/each}
- +
-
- {#each days as _, dayIndex} -
- {#each hours as __, hourIndex} -
-
+
+ {#each hours as _, hourIndex} + {#each days as _, dayIndex} + {@const value = heatmapData[hourIndex][dayIndex]} +
+ +
+ {value}%
- {/each} -
+
+ {/each} {/each}
-
+
{#each days as day} -
{day}
+
{day}
{/each}
-
-
-
- This month -
-
-
- Last month +
+
+
+ {#each [0.2, 0.4, 0.6, 0.8, 1] as opacity} +
+ {/each} +
+ Traffic intensity
- - +
+
diff --git a/apps/publisher-dashboard/src/lib/components/dashboard/performance-table.svelte b/apps/publisher-dashboard/src/lib/components/dashboard/performance-table.svelte index 7b318ed..1f5b3f5 100644 --- a/apps/publisher-dashboard/src/lib/components/dashboard/performance-table.svelte +++ b/apps/publisher-dashboard/src/lib/components/dashboard/performance-table.svelte @@ -1,9 +1,14 @@ - - -
-
- Performance by - - - -
- - - - {#each tabs as tab} - - {tab.label} - {#if tab.count} - - {tab.count} - - {/if} - - {/each} - - +
+ +
+
+

Performance by

+ + +
- - - - - - - Ad unit - Total revenue - -
- % of rev. - - - -
-
- Total impressions - % of impressions -
-
- - {#each tableData as row} - - -
-
- {row.name} - {row.revenue} - {row.revPercent} - {row.impressions} - {row.impPercent} -
- {/each} -
-
-
- + +
+ {#each tabs as tab} + {@const isActive = activeTab === tab.id} + + {/each} +
+
+ + +
+ {#if activeTab === "domain"} + + {:else if activeTab === "country"} + + {:else if activeTab === "source"} + + {:else if activeTab === "ad-unit"} + + {:else if activeTab === "key-value"} + + {/if} +
+
diff --git a/apps/publisher-dashboard/src/lib/components/dashboard/tabs/ad-unit-table.svelte b/apps/publisher-dashboard/src/lib/components/dashboard/tabs/ad-unit-table.svelte new file mode 100644 index 0000000..bba55ee --- /dev/null +++ b/apps/publisher-dashboard/src/lib/components/dashboard/tabs/ad-unit-table.svelte @@ -0,0 +1,108 @@ + + + + + + + Ad unit + Revenue + +
+ % of revenue + + + +
+
+ Impressions + % of impr. +
+
+ + {#each tableData as row, i} + + +
+ {i + 1} +
+
+ + {row.name} + + {row.revenue} + +
+
+
+
+ {row.revPercent.toFixed(2)}% +
+
+ {row.impressions} + {row.impPercent.toFixed(2)}% +
+ {/each} +
+
diff --git a/apps/publisher-dashboard/src/lib/components/dashboard/tabs/country-table.svelte b/apps/publisher-dashboard/src/lib/components/dashboard/tabs/country-table.svelte new file mode 100644 index 0000000..ce5d8e1 --- /dev/null +++ b/apps/publisher-dashboard/src/lib/components/dashboard/tabs/country-table.svelte @@ -0,0 +1,110 @@ + + + + + + + Country + Revenue + % of revenue + Impressions + % of impr. + + + + {#each tableData as row, i} + + +
+ {i + 1} +
+
+ +
+ {row.code} + {row.name} +
+
+ {row.revenue} + +
+
+
+
+ {row.revPercent.toFixed(2)}% +
+
+ {row.impressions} + {row.impPercent.toFixed(2)}% +
+ {/each} +
+
diff --git a/apps/publisher-dashboard/src/lib/components/dashboard/tabs/domain-table.svelte b/apps/publisher-dashboard/src/lib/components/dashboard/tabs/domain-table.svelte new file mode 100644 index 0000000..c653c92 --- /dev/null +++ b/apps/publisher-dashboard/src/lib/components/dashboard/tabs/domain-table.svelte @@ -0,0 +1,77 @@ + + + + + + + Domain + Revenue + % of revenue + Impressions + % of impr. + + + + {#each tableData as row, i} + + +
+ {i + 1} +
+
+ + {row.name} + + {row.revenue} + +
+
+
+
+ {row.revPercent.toFixed(2)}% +
+
+ {row.impressions} + {row.impPercent.toFixed(2)}% +
+ {/each} +
+
diff --git a/apps/publisher-dashboard/src/lib/components/dashboard/tabs/index.ts b/apps/publisher-dashboard/src/lib/components/dashboard/tabs/index.ts new file mode 100644 index 0000000..85e7ce5 --- /dev/null +++ b/apps/publisher-dashboard/src/lib/components/dashboard/tabs/index.ts @@ -0,0 +1,5 @@ +export { default as AdUnitTable } from "./ad-unit-table.svelte"; +export { default as CountryTable } from "./country-table.svelte"; +export { default as DomainTable } from "./domain-table.svelte"; +export { default as KeyValueTable } from "./key-value-table.svelte"; +export { default as SourceTable } from "./source-table.svelte"; diff --git a/apps/publisher-dashboard/src/lib/components/dashboard/tabs/key-value-table.svelte b/apps/publisher-dashboard/src/lib/components/dashboard/tabs/key-value-table.svelte new file mode 100644 index 0000000..d82bb5d --- /dev/null +++ b/apps/publisher-dashboard/src/lib/components/dashboard/tabs/key-value-table.svelte @@ -0,0 +1,102 @@ + + + + + + + Key + Value + Revenue + % of revenue + Impressions + % of impr. + + + + {#each tableData as row, i} + + +
+ {i + 1} +
+
+ + {row.key} + + + {row.value} + + {row.revenue} + +
+
+
+
+ {row.revPercent.toFixed(2)}% +
+
+ {row.impressions} + {row.impPercent.toFixed(2)}% +
+ {/each} +
+
diff --git a/apps/publisher-dashboard/src/lib/components/dashboard/tabs/source-table.svelte b/apps/publisher-dashboard/src/lib/components/dashboard/tabs/source-table.svelte new file mode 100644 index 0000000..ee400f1 --- /dev/null +++ b/apps/publisher-dashboard/src/lib/components/dashboard/tabs/source-table.svelte @@ -0,0 +1,93 @@ + + + + + + + Source + Revenue + % of revenue + Impressions + % of impr. + + + + {#each tableData as row, i} + + +
+ {i + 1} +
+
+ + {row.name} + + {row.revenue} + +
+
+
+
+ {row.revPercent.toFixed(2)}% +
+
+ {row.impressions} + {row.impPercent.toFixed(2)}% +
+ {/each} +
+
diff --git a/apps/publisher-dashboard/src/lib/components/layout/app-header.svelte b/apps/publisher-dashboard/src/lib/components/layout/app-header.svelte index e4b72cc..b416185 100644 --- a/apps/publisher-dashboard/src/lib/components/layout/app-header.svelte +++ b/apps/publisher-dashboard/src/lib/components/layout/app-header.svelte @@ -1,7 +1,8 @@
-

{title}

-
- - {#each filters as filter} - - {filter.label} - {#if filter.removable} - - {/if} - - {/each} + + +

{title}

+ + + {#if filters.length > 0} + + {/if} +
+ +
- - - - - + +
diff --git a/apps/publisher-dashboard/src/lib/components/layout/app-sidebar.svelte b/apps/publisher-dashboard/src/lib/components/layout/app-sidebar.svelte index 4294921..c4d8e1d 100644 --- a/apps/publisher-dashboard/src/lib/components/layout/app-sidebar.svelte +++ b/apps/publisher-dashboard/src/lib/components/layout/app-sidebar.svelte @@ -1,4 +1,6 @@