Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions apps/backend/router.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Router, Request, Response } from 'express';
import { VersionResolver } from './common/middleware/versionResolver';
import { UriVersionStrategy } from './common/strategies/uriStrategy';
import { HeaderVersionStrategy } from './common/strategies/headerStrategy';
import { VersionResolver } from './src/common/middleware/versionResolver';
import { UriVersionStrategy } from './src/common/strategies/uriStrategy';
import { HeaderVersionStrategy } from './src/common/strategies/headerStrategy';
import { resourceRouterV1 } from './v1/routes/resourceRouter';
import { resourceRouterV2 } from './v2/routes/resourceRouter';

Expand Down
2 changes: 1 addition & 1 deletion apps/backend/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import express, { Application } from 'express';
import { apiRouter } from './api/router';
import { apiRouter } from '../router';

const app: Application = express();

Expand Down
2 changes: 1 addition & 1 deletion apps/backend/v1/controllers/resourceController.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Request, Response } from 'express';
import { setDeprecationHeaders } from '../../common/utils/deprecation';
import { setDeprecationHeaders } from '../../src/common/utils/deprecation';

export class ResourceControllerV1 {
public static getResources(req: Request, res: Response): void {
Expand Down
18 changes: 9 additions & 9 deletions apps/dashboard/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,46 +9,46 @@
}

body {
@apply bg-gray-950 text-gray-100;
@apply bg-white text-gray-900 dark:bg-gray-950 dark:text-gray-100;
}

/* Scrollbar styling */
::-webkit-scrollbar {
@apply w-1.5 h-1.5;
}
::-webkit-scrollbar-track {
@apply bg-gray-900;
@apply bg-gray-100 dark:bg-gray-900;
}
::-webkit-scrollbar-thumb {
@apply bg-gray-700 rounded-full;
@apply bg-gray-300 dark:bg-gray-700 rounded-full;
}
::-webkit-scrollbar-thumb:hover {
@apply bg-gray-600;
@apply bg-gray-400 dark:bg-gray-600;
}
}

@layer components {
.sentinel-card {
@apply bg-gray-900 border border-gray-800 rounded-xl p-6;
@apply bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-xl p-6;
}

.sentinel-badge {
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
}

.sentinel-badge--critical {
@apply bg-red-950 text-red-400 border border-red-800;
@apply bg-red-50 text-red-700 border border-red-200 dark:bg-red-950 dark:text-red-400 dark:border-red-800;
}

.sentinel-badge--high {
@apply bg-orange-950 text-orange-400 border border-orange-800;
@apply bg-orange-50 text-orange-700 border border-orange-200 dark:bg-orange-950 dark:text-orange-400 dark:border-orange-800;
}

.sentinel-badge--medium {
@apply bg-yellow-950 text-yellow-400 border border-yellow-800;
@apply bg-yellow-50 text-yellow-700 border border-yellow-200 dark:bg-yellow-950 dark:text-yellow-400 dark:border-yellow-800;
}

.sentinel-badge--low {
@apply bg-blue-950 text-blue-400 border border-blue-800;
@apply bg-blue-50 text-blue-700 border border-blue-200 dark:bg-blue-950 dark:text-blue-400 dark:border-blue-800;
}
}
35 changes: 19 additions & 16 deletions apps/dashboard/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Metadata } from 'next';
import './globals.css';
import { Sidebar } from '@/components/layout/Sidebar';
import { TopBar } from '@/components/layout/TopBar';
import { ThemeProvider } from '@/components/theme/ThemeProvider';

export const metadata: Metadata = {
title: {
Expand All @@ -20,23 +21,25 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en" className="h-full">
<body className="h-full flex overflow-hidden">
{/* Sidebar */}
<Sidebar />
<html lang="en" className="h-full dark" suppressHydrationWarning>
<body className="h-full flex overflow-hidden bg-white dark:bg-gray-950 text-gray-900 dark:text-gray-100">
<ThemeProvider>
{/* Sidebar */}
<Sidebar />

{/* Main content area */}
<div className="flex-1 flex flex-col min-w-0 overflow-hidden">
<TopBar />
<main
id="main-content"
className="flex-1 overflow-y-auto p-6 space-y-6"
role="main"
aria-label="Dashboard content"
>
{children}
</main>
</div>
{/* Main content area */}
<div className="flex-1 flex flex-col min-w-0 overflow-hidden">
<TopBar />
<main
id="main-content"
className="flex-1 overflow-y-auto p-6 space-y-6"
role="main"
aria-label="Dashboard content"
>
{children}
</main>
</div>
</ThemeProvider>
</body>
</html>
);
Expand Down
16 changes: 9 additions & 7 deletions apps/dashboard/src/components/layout/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,11 @@ export function Sidebar() {

return (
<nav
className="w-60 flex-shrink-0 bg-gray-900 border-r border-gray-800 flex flex-col"
className="w-60 flex-shrink-0 bg-gray-50 dark:bg-gray-900 border-r border-gray-200 dark:border-gray-800 flex flex-col"
aria-label="Main navigation"
>
{/* Logo */}
<div className="h-16 flex items-center px-5 border-b border-gray-800">
<div className="h-16 flex items-center px-5 border-b border-gray-200 dark:border-gray-800">
<div className="flex items-center gap-2.5">
<div className="w-8 h-8 rounded-lg bg-sentinel-600 flex items-center justify-center">
<svg
Expand All @@ -165,7 +165,9 @@ export function Sidebar() {
/>
</svg>
</div>
<span className="text-white font-semibold text-lg tracking-tight">Sentinel</span>
<span className="text-gray-900 dark:text-white font-semibold text-lg tracking-tight">
Sentinel
</span>
</div>
</div>

Expand All @@ -181,8 +183,8 @@ export function Sidebar() {
className={[
'flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors',
isActive
? 'bg-sentinel-700/30 text-sentinel-300 border border-sentinel-700/50'
: 'text-gray-400 hover:bg-gray-800 hover:text-gray-100',
? 'bg-sentinel-700/30 text-sentinel-600 dark:text-sentinel-300 border border-sentinel-700/50'
: 'text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-900 dark:hover:text-gray-100',
].join(' ')}
>
{item.icon}
Expand All @@ -194,8 +196,8 @@ export function Sidebar() {
</ul>

{/* Footer */}
<div className="px-5 py-4 border-t border-gray-800">
<p className="text-xs text-gray-600">Sentinel v0.1.0</p>
<div className="px-5 py-4 border-t border-gray-200 dark:border-gray-800">
<p className="text-xs text-gray-400 dark:text-gray-600">Sentinel v0.1.0</p>
</div>
</nav>
);
Expand Down
12 changes: 8 additions & 4 deletions apps/dashboard/src/components/layout/TopBar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import { usePathname } from 'next/navigation';
import { ThemeToggle } from '@/components/theme/ThemeToggle';

const pageTitles: Record<string, string> = {
'/': 'Overview',
Expand All @@ -15,12 +16,12 @@ export function TopBar() {
const title = pageTitles[pathname] ?? 'Dashboard';

return (
<header className="h-16 flex-shrink-0 bg-gray-900 border-b border-gray-800 flex items-center justify-between px-6">
<header className="h-16 flex-shrink-0 bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800 flex items-center justify-between px-6">
{/* Breadcrumb / page title */}
<div className="flex items-center gap-2 text-sm text-gray-400">
<span>Sentinel</span>
<div className="flex items-center gap-2 text-sm text-gray-400 dark:text-gray-400">
<span className="text-gray-500 dark:text-gray-400">Sentinel</span>
<svg
className="w-4 h-4 text-gray-600"
className="w-4 h-4 text-gray-300 dark:text-gray-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
Expand All @@ -33,6 +34,9 @@ export function TopBar() {

{/* Right actions */}
<div className="flex items-center gap-3">
{/* Theme toggle */}
<ThemeToggle />

{/* Live indicator */}
<div
className="flex items-center gap-1.5 text-xs text-emerald-400"
Expand Down
48 changes: 48 additions & 0 deletions apps/dashboard/src/components/theme/ThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use client';

import { createContext, useContext, useEffect, useState, type ReactNode } from 'react';

type Theme = 'light' | 'dark';

interface ThemeContextType {
theme: Theme;
toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

export function useTheme() {
const ctx = useContext(ThemeContext);
if (!ctx) throw new Error('useTheme must be used within ThemeProvider');
return ctx;
}

export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState<Theme>('dark');
const [mounted, setMounted] = useState(false);

useEffect(() => {
const stored = localStorage.getItem('sentinel-theme') as Theme | null;
const initial = stored ?? 'dark';
setTheme(initial);
document.documentElement.classList.toggle('dark', initial === 'dark');
document.documentElement.classList.toggle('light', initial === 'light');
setMounted(true);
}, []);

const toggleTheme = () => {
setTheme(prev => {
const next = prev === 'dark' ? 'light' : 'dark';
localStorage.setItem('sentinel-theme', next);
document.documentElement.classList.remove('dark', 'light');
document.documentElement.classList.add(next);
return next;
});
};

if (!mounted) {
return <>{children}</>;
}

return <ThemeContext.Provider value={{ theme, toggleTheme }}>{children}</ThemeContext.Provider>;
}
49 changes: 49 additions & 0 deletions apps/dashboard/src/components/theme/ThemeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use client';

import { useTheme } from './ThemeProvider';

export function ThemeToggle() {
const { theme, toggleTheme } = useTheme();

return (
<button
type="button"
onClick={toggleTheme}
className="p-2 rounded-lg text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-900 dark:hover:text-gray-100 transition-colors"
aria-label={`Switch to ${theme === 'dark' ? 'light' : 'dark'} mode`}
title={`Switch to ${theme === 'dark' ? 'light' : 'dark'} mode`}
>
{theme === 'dark' ? (
<svg
className="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"
/>
</svg>
) : (
<svg
className="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
/>
</svg>
)}
</button>
);
}
1 change: 1 addition & 0 deletions apps/dashboard/tailwind.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Config } from 'tailwindcss';

const config: Config = {
darkMode: 'class',
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
Expand Down
1 change: 1 addition & 0 deletions jest.backend.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module.exports = {
testEnvironment: 'node',
rootDir: '.',
testMatch: ['<rootDir>/apps/backend/**/*.spec.ts', '<rootDir>/src/**/*.spec.ts'],
testPathIgnorePatterns: ['/node_modules/', '/src/api-keys/'],
transform: {
'^.+\\.ts$': [
'ts-jest',
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"apps/backend/**/*",
"libs/**/*",
"database/**/*",
"src/**/*",
"src/modules/reports/**/*",
"tests/**/*",
"prisma.config.ts"
],
Expand Down
Loading