Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
90e9f1a
DPS-67: Design system foundation — tokens, typography & shared styles
dantheuber Mar 6, 2026
60a7186
DPS-68: Header & footer refinement — extract components, design token…
dantheuber Mar 6, 2026
b97db9a
DPS-73: Modal & ConfirmDialog visual polish — design tokens, Lucide i…
dantheuber Mar 6, 2026
133c69a
DPS-69: Dashboard layout stability & polish — stable CSS Grid, design…
dantheuber Mar 6, 2026
ea945a3
DPS-70: Add reusable Tabs component with URL-persisted state
dantheuber Mar 6, 2026
5a0ceb4
DPS-70: Refactor TeamDetail into tabbed layout with tests
dantheuber Mar 6, 2026
639d03a
DPS-71: Refactor ServiceDetail into tabbed layout with tests
dantheuber Mar 6, 2026
b92aa19
DPS-71: Add dependency detail modal with latency chart and contact info
dantheuber Mar 6, 2026
b52d4ed
DPS-71: Add tests for URL param tab selection and dependency detail m…
dantheuber Mar 6, 2026
f80d85a
DPS-72: Polish graph toolbar, settings dropdown, and side panels
dantheuber Mar 6, 2026
cfd4faf
DPS-74: Polish Services list page with design tokens and Lucide icons
dantheuber Mar 6, 2026
d52012e
DPS-74: Polish Teams list page with design tokens and Lucide icons
dantheuber Mar 6, 2026
3c7b8c9
DPS-74: Polish Wallboard page with design tokens and Lucide icons
dantheuber Mar 6, 2026
a89aeb9
DPS-74: Polish Catalog page with design tokens and Lucide icons
dantheuber Mar 6, 2026
95faea4
DPS-74: Polish Admin, Associations, and Manifest pages with design to…
dantheuber Mar 6, 2026
9c8b688
DPS-74: Polish Login and NotFound pages with design tokens and Lucide…
dantheuber Mar 6, 2026
cd7716b
DPS-74: Polish SearchableSelect and StatusBadge with design tokens an…
dantheuber Mar 6, 2026
9772b2d
DPS-74: Final audit — replace hardcoded values, transition durations,…
dantheuber Mar 6, 2026
ba8a351
DPS-74: Update client architecture spec and README with design tokens…
dantheuber Mar 6, 2026
6b24658
UI polish pass
dantheuber Mar 6, 2026
8be65e6
1.11.1
dantheuber Mar 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ For detailed deployment options (bare Node.js, reverse proxy, backups), see the

## Tech Stack

- **Frontend:** React 18, TypeScript, Vite, CSS Modules, React Flow, Recharts
- **Frontend:** React 18, TypeScript, Vite, CSS Modules (design tokens + `color-mix()`), Lucide React icons, Inter font, React Flow, Recharts
- **Backend:** Express.js, TypeScript, SQLite (better-sqlite3)
- **Authentication:** OpenID Connect (openid-client) or local auth (bcryptjs)
- **Testing:** Jest, React Testing Library
Expand Down
3 changes: 3 additions & 0 deletions client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/depsera-sphere.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
<title>Depsera</title>
</head>
<body>
Expand Down
10 changes: 10 additions & 0 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@xyflow/react": "^12.10.0",
"dagre": "^0.8.5",
"elkjs": "^0.11.0",
"lucide-react": "^0.577.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.23.1",
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/Charts/HealthTimeline.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
color: var(--color-accent);
font-size: 12px;
cursor: pointer;
transition: all 0.15s;
transition: all var(--duration-fast);
}

.retryButton:hover {
Expand All @@ -88,7 +88,7 @@
position: relative;
min-width: 2px;
cursor: pointer;
transition: opacity 0.15s;
transition: opacity var(--duration-fast);
}

.segment:hover {
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/Charts/LatencyChart.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
color: var(--color-accent);
font-size: 12px;
cursor: pointer;
transition: all 0.15s;
transition: all var(--duration-fast);
}

.retryButton:hover {
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/Charts/TimeRangeSelector.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
font-size: 12px;
font-weight: 500;
cursor: pointer;
transition: all 0.15s;
transition: all var(--duration-fast);
line-height: 1.4;
}

Expand Down
12 changes: 12 additions & 0 deletions client/src/components/Layout/Footer.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.footer {
height: 28px;
display: flex;
align-items: center;
justify-content: center;
padding: 0 var(--space-4);
border-top: 1px solid var(--color-border-subtle);
background-color: var(--color-surface);
font-size: var(--font-xs);
color: var(--color-text-muted);
flex-shrink: 0;
}
11 changes: 11 additions & 0 deletions client/src/components/Layout/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import styles from './Footer.module.css';

function Footer() {
return (
<footer className={styles.footer}>
Depsera v{__APP_VERSION__}
</footer>
);
}

export default Footer;
227 changes: 227 additions & 0 deletions client/src/components/Layout/Header.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
.header {
height: 48px;
background-color: var(--color-surface);
border-bottom: 1px solid var(--color-border);
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 var(--space-4);
position: sticky;
top: 0;
z-index: 100;
flex-shrink: 0;
}

.headerLeft {
display: flex;
align-items: center;
gap: var(--space-3);
}

.menuButton {
display: none;
background: none;
border: none;
padding: var(--space-1);
color: var(--color-text-secondary);
transition: color var(--duration-fast) ease;
}

.menuButton:hover {
color: var(--color-text);
}

.menuIcon {
display: block;
width: 20px;
height: 2px;
background-color: currentColor;
position: relative;
}

.menuIcon::before,
.menuIcon::after {
content: '';
position: absolute;
width: 20px;
height: 2px;
background-color: currentColor;
left: 0;
}

.menuIcon::before {
top: -6px;
}

.menuIcon::after {
top: 6px;
}

.logo {
width: 32px;
height: 32px;
}

.titleImage {
height: 18px;
width: auto;
}

.headerRight {
display: flex;
align-items: center;
gap: var(--space-3);
}

.userInfo {
display: flex;
align-items: center;
gap: var(--space-2);
}

.userName {
font-size: var(--font-sm);
font-weight: var(--font-medium);
color: var(--color-text);
}

.userRole {
font-size: var(--font-xs);
color: var(--color-text-muted);
background-color: var(--color-surface-hover);
padding: 2px var(--space-2);
border-radius: var(--radius-sm);
text-transform: capitalize;
}

/* Theme Toggle — pill-shaped */
.themeToggle {
display: flex;
align-items: center;
width: 56px;
height: 28px;
padding: 2px;
background-color: var(--color-surface-hover);
border: 1px solid var(--color-border);
border-radius: 14px;
cursor: pointer;
position: relative;
transition: background-color var(--duration-fast) ease, border-color var(--duration-fast) ease;
}

.themeToggle:hover {
border-color: var(--color-accent);
}

.themeToggleTrack {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 100%;
position: relative;
}

.themeToggleIcon {
display: flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
border-radius: 50%;
z-index: 1;
color: var(--color-text-muted);
transition: color var(--duration-normal) ease;
}

.themeToggleIcon.active {
color: var(--color-accent);
}

.themeToggleIndicator {
position: absolute;
width: 22px;
height: 22px;
border-radius: 50%;
background-color: var(--color-surface);
box-shadow: var(--shadow-sm);
transition: transform var(--duration-normal) ease;
top: 1px;
left: 1px;
}

.themeToggleIndicator.dark {
transform: translateX(28px);
}

.themeIcon {
width: 14px;
height: 14px;
}

/* Logout Button */
.logoutButton {
font-size: var(--font-sm);
font-weight: var(--font-medium);
padding: var(--space-1) var(--space-3);
border-radius: var(--radius-sm);
background: transparent;
color: var(--color-text-secondary);
border: 1px solid var(--color-border);
cursor: pointer;
transition: background-color var(--duration-fast) ease, color var(--duration-fast) ease;
}

.logoutButton:hover {
background-color: var(--color-surface-hover);
color: var(--color-text);
}

/* Responsive */
@media (max-width: 768px) {
.menuButton {
display: flex;
align-items: center;
justify-content: center;
}

.userName {
display: none;
}

.userRole {
display: none;
}

.headerRight {
gap: var(--space-2);
}

.logoutButton {
padding: var(--space-1) var(--space-2);
}

.themeToggle {
width: 48px;
height: 24px;
}

.themeToggleIcon {
width: 18px;
height: 18px;
}

.themeToggleIndicator {
width: 18px;
height: 18px;
}

.themeToggleIndicator.dark {
transform: translateX(24px);
}

.themeIcon {
width: 12px;
height: 12px;
}
}
57 changes: 57 additions & 0 deletions client/src/components/Layout/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Sun, Moon, LogOut } from 'lucide-react';
import { useAuth } from '../../contexts/AuthContext';
import { useTheme } from '../../contexts/ThemeContext';
import styles from './Header.module.css';

interface HeaderProps {
onToggleSidebar: () => void;
onLogout: () => void;
}

function Header({ onToggleSidebar, onLogout }: HeaderProps) {
const { user } = useAuth();
const { theme, toggleTheme } = useTheme();

return (
<header className={styles.header}>
<div className={styles.headerLeft}>
<button
className={styles.menuButton}
onClick={onToggleSidebar}
aria-label="Toggle navigation"
>
<span className={styles.menuIcon} />
</button>
<img src="/depsera-sphere.svg" alt="" className={styles.logo} />
<img src="/depsera-title-thin.svg" alt="Depsera" className={styles.titleImage} />
</div>
<div className={styles.headerRight}>
<div className={styles.userInfo}>
<span className={styles.userName}>{user?.name}</span>
<span className={styles.userRole}>{user?.role}</span>
</div>
<button
className={styles.themeToggle}
onClick={toggleTheme}
aria-label={`Switch to ${theme === 'light' ? 'dark' : 'light'} theme`}
title={`Switch to ${theme === 'light' ? 'dark' : 'light'} theme`}
>
<div className={styles.themeToggleTrack}>
<span className={`${styles.themeToggleIcon} ${theme === 'light' ? styles.active : ''}`}>
<Sun className={styles.themeIcon} />
</span>
<span className={`${styles.themeToggleIcon} ${theme === 'dark' ? styles.active : ''}`}>
<Moon className={styles.themeIcon} />
</span>
</div>
<span className={`${styles.themeToggleIndicator} ${theme === 'dark' ? styles.dark : ''}`} />
</button>
<button className={styles.logoutButton} onClick={onLogout}>
<LogOut size={14} />
</button>
</div>
</header>
);
}

export default Header;
Loading
Loading