Skip to content

Commit de6eb70

Browse files
committed
dashboard versioning and deps
1 parent 923661f commit de6eb70

38 files changed

Lines changed: 3212 additions & 2068 deletions

packages/dashboard/app/app.css

Lines changed: 328 additions & 37 deletions
Large diffs are not rendered by default.

packages/dashboard/app/components/queue-stats-cards.tsx

Lines changed: 0 additions & 91 deletions
This file was deleted.

packages/dashboard/app/components/sidebar.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -231,11 +231,14 @@ export function AppSidebar () {
231231
return (
232232
<Sidebar>
233233
<SidebarHeader>
234-
<div className="flex items-center gap-2 px-2 py-2">
235-
<div className="w-8 h-8 rounded-lg bg-primary-600 flex items-center justify-center shrink-0">
236-
<span className="text-white font-bold text-sm">PG</span>
234+
<div className="flex items-center gap-2.5 pl-1.5 pr-3 py-2">
235+
<div className="w-8 h-8 rounded-[9px] bg-primary-600 flex items-center justify-center shrink-0">
236+
<span className="text-white font-bold text-sm tracking-tight">PG</span>
237+
</div>
238+
<div className="leading-tight whitespace-nowrap group-data-[state=collapsed]:hidden">
239+
<div className="font-semibold text-sm text-sidebar-accent-foreground">pg-boss</div>
240+
<div className="font-mono text-[9px] tracking-[0.15em] text-sidebar-foreground/60">CONSOLE</div>
237241
</div>
238-
<span className="font-semibold text-sidebar-foreground group-data-[state=collapsed]:hidden">pg-boss</span>
239242
</div>
240243
{databases && currentDb && databases.length > 1 && (
241244
<div className="group-data-[state=collapsed]:hidden">
@@ -262,7 +265,7 @@ export function AppSidebar () {
262265
{({ isActive }: { isActive: boolean }) => (
263266
<SidebarMenuButton isActive={isActive} tooltip={item.name}>
264267
<item.icon className="h-5 w-5 flex-shrink-0" />
265-
<span className="group-data-[state=collapsed]:hidden">{item.name}</span>
268+
<span className="whitespace-nowrap group-data-[state=collapsed]:hidden">{item.name}</span>
266269
</SidebarMenuButton>
267270
)}
268271
</NavLink>
Lines changed: 11 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,29 @@
11
import type { QueueStats } from '~/lib/types'
2-
import { cn } from '~/lib/utils'
2+
import { StatCard } from '~/components/ui/stat-card'
33

44
interface StatsCardsProps {
55
stats: QueueStats
66
}
77

88
const statCards = [
9-
{
10-
name: 'Queued Jobs',
11-
key: 'totalQueued' as const,
12-
description: 'Jobs waiting to be processed',
13-
color: 'text-gray-600 dark:text-gray-400',
14-
bgColor: 'bg-gray-50 dark:bg-gray-800',
15-
},
16-
{
17-
name: 'Active Jobs',
18-
key: 'totalActive' as const,
19-
description: 'Jobs currently processing',
20-
color: 'text-gray-600 dark:text-gray-400',
21-
bgColor: 'bg-gray-50 dark:bg-gray-800',
22-
},
23-
{
24-
name: 'Deferred Jobs',
25-
key: 'totalDeferred' as const,
26-
description: 'Jobs scheduled for later',
27-
color: 'text-gray-600 dark:text-gray-400',
28-
bgColor: 'bg-gray-50 dark:bg-gray-800',
29-
},
30-
{
31-
name: 'Total Jobs',
32-
key: 'totalJobs' as const,
33-
description: 'All jobs across queues',
34-
color: 'text-gray-600 dark:text-gray-400',
35-
bgColor: 'bg-gray-50 dark:bg-gray-800',
36-
},
9+
{ name: 'Queued Jobs', key: 'totalQueued' as const, hint: 'waiting to process', accent: 'neutral' as const },
10+
{ name: 'Active', key: 'totalActive' as const, hint: 'processing now', accent: 'primary' as const },
11+
{ name: 'Deferred', key: 'totalDeferred' as const, hint: 'scheduled for later', accent: 'neutral' as const },
12+
{ name: 'Total Jobs', key: 'totalJobs' as const, hint: 'all-time across queues', accent: 'neutral' as const },
3713
]
3814

3915
export function StatsCards ({ stats }: StatsCardsProps) {
4016
return (
4117
<>
4218
{statCards.map((stat) => (
43-
<div
19+
<StatCard
4420
key={stat.key}
45-
className={cn(
46-
'overflow-hidden rounded-xl border shadow-sm',
47-
'bg-white border-gray-200',
48-
'dark:bg-gray-900 dark:border-gray-800'
49-
)}
50-
>
51-
<div className="p-5">
52-
<div className="flex items-center">
53-
<div className={cn('flex-shrink-0 rounded-lg p-3', stat.bgColor)}>
54-
<StatIcon className={cn('h-6 w-6', stat.color)} />
55-
</div>
56-
<div className="ml-4 flex-1">
57-
<p className="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">
58-
{stat.name}
59-
</p>
60-
<p className={cn('text-2xl font-semibold', stat.color)}>
61-
{stats[stat.key].toLocaleString()}
62-
</p>
63-
</div>
64-
</div>
65-
</div>
66-
</div>
21+
label={stat.name}
22+
value={stats[stat.key].toLocaleString()}
23+
hint={stat.hint}
24+
accent={stat.accent}
25+
/>
6726
))}
6827
</>
6928
)
7029
}
71-
72-
function StatIcon ({ className }: { className?: string }) {
73-
return (
74-
<svg
75-
className={className}
76-
fill="none"
77-
viewBox="0 0 24 24"
78-
strokeWidth={1.5}
79-
stroke="currentColor"
80-
>
81-
<path
82-
strokeLinecap="round"
83-
strokeLinejoin="round"
84-
d="M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 0 1 3 19.875v-6.75ZM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V8.625ZM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V4.125Z"
85-
/>
86-
</svg>
87-
)
88-
}

packages/dashboard/app/components/theme-provider.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@ import { createContext, useContext, useEffect, useState } from 'react'
22

33
type Theme = 'light' | 'dark' | 'system'
44

5-
export type ColorTheme = 'emerald' | 'teal' | 'cyan' | 'sky' | 'blue' | 'indigo' | 'violet' | 'purple'
5+
export type ColorTheme = 'cobalt' | 'emerald' | 'teal' | 'cyan' | 'sky' | 'blue' | 'indigo' | 'violet' | 'purple'
66

7-
// Ordered by hue (warm to cool to warm)
7+
// Cobalt is the console's signature primary; the rest follow by hue.
88
export const COLOR_THEMES: ColorTheme[] = [
9-
'emerald', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'violet', 'purple'
9+
'cobalt', 'emerald', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'violet', 'purple'
1010
]
1111

12-
// Tailwind color-600 hex values for favicon
12+
// Color-600 hex values for favicon
1313
const COLOR_HEX: Record<ColorTheme, string> = {
14+
cobalt: '#284fe0',
1415
emerald: '#059669',
1516
teal: '#0d9488',
1617
cyan: '#0891b2',
@@ -54,7 +55,7 @@ function getStoredColorTheme (): ColorTheme {
5455
if (stored && COLOR_THEMES.includes(stored as ColorTheme)) {
5556
return stored as ColorTheme
5657
}
57-
return 'violet'
58+
return 'cobalt'
5859
}
5960

6061
export function ThemeProvider ({ children }: { children: React.ReactNode }) {

packages/dashboard/app/components/ui/badge.tsx

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,22 @@ import { cva, type VariantProps } from 'class-variance-authority'
22
import { type ReactNode } from 'react'
33
import { cn } from '~/lib/utils'
44

5+
// Variants map onto the pg-boss job-lifecycle palette (--state-*), which is
6+
// theme-aware (solid tints in light, translucent on dark), so the same five
7+
// variants render correctly in both modes without per-mode classes.
58
const badgeVariants = cva(
6-
'inline-flex items-center gap-1.5 rounded-full font-medium ring-1 ring-inset',
9+
'inline-flex items-center gap-1.5 rounded-full font-medium',
710
{
811
variants: {
912
variant: {
10-
gray: 'bg-gray-100 text-gray-700 ring-gray-500/10 dark:bg-gray-800 dark:text-gray-300 dark:ring-gray-500/20',
11-
primary: 'bg-primary-50 text-primary-700 ring-primary-600/10 dark:bg-primary-950 dark:text-primary-300 dark:ring-primary-400/20',
12-
success: 'bg-green-50 text-green-700 ring-green-600/10 dark:bg-green-950 dark:text-green-300 dark:ring-green-400/20',
13-
warning: 'bg-warning-50 text-amber-700 ring-warning-600/10 dark:bg-warning-950 dark:text-amber-300 dark:ring-warning-400/20',
14-
error: 'bg-red-50 text-red-700 ring-red-600/10 dark:bg-red-950 dark:text-red-300 dark:ring-red-400/20',
13+
gray: 'bg-[var(--state-created-bg)] text-[var(--state-created-fg)]',
14+
primary: 'bg-[var(--state-active-bg)] text-[var(--state-active-fg)]',
15+
success: 'bg-[var(--state-completed-bg)] text-[var(--state-completed-fg)]',
16+
warning: 'bg-[var(--state-retry-bg)] text-[var(--state-retry-fg)]',
17+
error: 'bg-[var(--state-failed-bg)] text-[var(--state-failed-fg)]',
1518
},
1619
size: {
17-
sm: 'px-2 py-0.5 text-xs',
20+
sm: 'px-2 py-0.5 text-[11px]',
1821
md: 'px-2.5 py-1 text-xs',
1922
lg: 'px-3 py-1 text-sm',
2023
},
@@ -27,11 +30,11 @@ const badgeVariants = cva(
2730
)
2831

2932
const dotVariants: Record<NonNullable<VariantProps<typeof badgeVariants>['variant']>, string> = {
30-
gray: 'bg-gray-500',
31-
primary: 'bg-primary-500 dark:bg-primary-400',
32-
success: 'bg-green-500',
33-
warning: 'bg-warning-500',
34-
error: 'bg-red-500',
33+
gray: 'bg-[var(--state-created-dot)]',
34+
primary: 'bg-[var(--state-active-dot)]',
35+
success: 'bg-[var(--state-completed-dot)]',
36+
warning: 'bg-[var(--state-retry-dot)]',
37+
error: 'bg-[var(--state-failed-dot)]',
3538
}
3639

3740
interface BadgeProps extends VariantProps<typeof badgeVariants> {

packages/dashboard/app/components/ui/card.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ interface CardContentProps extends HTMLAttributes<HTMLDivElement> {
2626
className?: string
2727
}
2828

29-
export function Card ({ children, className, ...props }: CardProps) {
29+
export function Card ({ children, className, style, ...props }: CardProps) {
3030
return (
3131
<div
3232
className={cn(
33-
'bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden',
34-
'dark:bg-gray-900 dark:border-gray-800',
33+
'rounded-[10px] border border-[var(--border-default)] shadow-sm overflow-hidden',
3534
className
3635
)}
36+
style={{ background: 'var(--surface-card-grad)', ...style }}
3737
{...props}
3838
>
3939
{children}
@@ -45,7 +45,7 @@ export function CardHeader ({ children, className, ...props }: CardHeaderProps)
4545
return (
4646
<div
4747
className={cn(
48-
'px-6 py-5 border-b border-gray-200 dark:border-gray-800',
48+
'flex items-center justify-between gap-3 px-5 py-4 border-b border-[var(--border-subtle)]',
4949
className
5050
)}
5151
{...props}
@@ -59,7 +59,7 @@ export function CardTitle ({ children, className, ...props }: CardTitleProps) {
5959
return (
6060
<h3
6161
className={cn(
62-
'text-lg font-semibold text-gray-900 dark:text-gray-100',
62+
'text-base font-semibold tracking-[-0.01em] text-[var(--text-primary)]',
6363
className
6464
)}
6565
{...props}
@@ -76,7 +76,7 @@ export function CardDescription ({
7676
}: CardDescriptionProps) {
7777
return (
7878
<p
79-
className={cn('mt-1 text-sm text-gray-500 dark:text-gray-400', className)}
79+
className={cn('mt-1 text-sm text-[var(--text-tertiary)]', className)}
8080
{...props}
8181
>
8282
{children}
@@ -86,7 +86,7 @@ export function CardDescription ({
8686

8787
export function CardContent ({ children, className, ...props }: CardContentProps) {
8888
return (
89-
<div className={cn('px-6 py-5', className)} {...props}>
89+
<div className={cn('px-5 py-5', className)} {...props}>
9090
{children}
9191
</div>
9292
)

packages/dashboard/app/components/ui/color-theme-picker.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { cn } from '~/lib/utils'
44

55
// Map color names to their Tailwind CSS variable for the 500 shade
66
const colorSwatchStyles: Record<ColorTheme, string> = {
7+
cobalt: 'bg-[#3b66f5]',
78
emerald: 'bg-emerald-500',
89
teal: 'bg-teal-500',
910
cyan: 'bg-cyan-500',
@@ -15,6 +16,7 @@ const colorSwatchStyles: Record<ColorTheme, string> = {
1516
}
1617

1718
const colorLabels: Record<ColorTheme, string> = {
19+
cobalt: 'Cobalt',
1820
emerald: 'Emerald',
1921
teal: 'Teal',
2022
cyan: 'Cyan',
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { type ReactNode } from 'react'
2+
3+
interface PageHeaderProps {
4+
title: ReactNode
5+
subtitle?: ReactNode
6+
action?: ReactNode
7+
}
8+
9+
// Route header — title + optional subtitle on the left, a primary action on
10+
// the right. Leads every console view.
11+
export function PageHeader ({ title, subtitle, action }: PageHeaderProps) {
12+
return (
13+
<div className="flex items-start justify-between gap-4 mb-6">
14+
<div>
15+
<h1 className="text-2xl font-semibold tracking-[-0.02em] text-[var(--text-primary)]">
16+
{title}
17+
</h1>
18+
{subtitle && (
19+
<p className="mt-1 text-sm text-[var(--text-tertiary)]">{subtitle}</p>
20+
)}
21+
</div>
22+
{action}
23+
</div>
24+
)
25+
}

0 commit comments

Comments
 (0)