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
17 changes: 4 additions & 13 deletions app/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use client'

import { Bot, LayoutDashboard, LogOut, Moon, Plus, Share2, Sun, User } from 'lucide-react'
import { Bot, LayoutDashboard, LogOut, Plus, Share2, User } from 'lucide-react'
import { ReactNode } from 'react'
import { useAuth } from '../contexts/AuthContext'
import { useTheme } from '../contexts/ThemeContext'
import { ThemeToggle } from './ui/ThemeToggle'

type Page = 'dashboard' | 'add' | 'shared' | 'ai' | 'profile'

Expand All @@ -23,7 +23,6 @@ const navItems = [

export function Layout({ children, currentPage, onNavigate }: LayoutProps) {
const { signOut } = useAuth()
const { theme, toggleTheme } = useTheme()

return (
<div className="min-h-screen bg-stone-50 text-slate-950 dark:bg-slate-950 dark:text-slate-50">
Expand Down Expand Up @@ -68,13 +67,7 @@ export function Layout({ children, currentPage, onNavigate }: LayoutProps) {
</nav>

<div className="mt-auto space-y-2 border-t border-slate-200 pt-4 dark:border-slate-800">
<button
onClick={toggleTheme}
className="flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-semibold text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-950 dark:text-slate-300 dark:hover:bg-slate-900 dark:hover:text-white"
>
{theme === 'light' ? <Moon className="h-4 w-4" /> : <Sun className="h-4 w-4" />}
{theme === 'light' ? 'Dark mode' : 'Light mode'}
</button>
<ThemeToggle variant="row" />
<button
onClick={signOut}
className="flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-semibold text-slate-600 transition-colors hover:bg-red-50 hover:text-red-600 dark:text-slate-300 dark:hover:bg-red-950/30 dark:hover:text-red-300"
Expand All @@ -95,9 +88,7 @@ export function Layout({ children, currentPage, onNavigate }: LayoutProps) {
</div>
</div>
<div className="flex items-center gap-1">
<button onClick={toggleTheme} className="rounded-lg p-2 text-slate-500 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-900" aria-label="Toggle theme">
{theme === 'light' ? <Moon className="h-5 w-5" /> : <Sun className="h-5 w-5" />}
</button>
<ThemeToggle variant="pill" />
<button onClick={signOut} className="rounded-lg p-2 text-slate-500 hover:bg-red-50 hover:text-red-600 dark:text-slate-300 dark:hover:bg-red-950/30 dark:hover:text-red-300" aria-label="Logout">
<LogOut className="h-5 w-5" />
</button>
Expand Down
29 changes: 29 additions & 0 deletions app/components/ui/LegalNav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use client'

import { ArrowLeft } from 'lucide-react'
import Link from 'next/link'
import { ThemeToggle } from './ThemeToggle'

export function LegalNav() {
return (
<header className="sticky top-0 z-50 border-b border-slate-200 bg-white/90 backdrop-blur-md dark:border-slate-800 dark:bg-slate-950/90">
<div className="mx-auto flex max-w-5xl items-center justify-between px-4 py-3 sm:px-6 lg:px-8">
<Link href="/" className="flex items-center gap-2.5">
<img src="/logo.png" alt="DumpIt" className="h-8 w-8 object-contain" />
<span className="text-base font-bold text-slate-900 dark:text-white">DumpIt</span>
</Link>
<div className="flex items-center gap-2">
<ThemeToggle variant="pill" />
<Link
href="/"
className="inline-flex items-center gap-1.5 rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm font-medium text-slate-700 shadow-sm transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-200 dark:hover:bg-slate-800"
>
<ArrowLeft className="h-3.5 w-3.5" />
<span className="hidden sm:inline">Back to Home</span>
<span className="sm:hidden">Home</span>
</Link>
</div>
</div>
</header>
)
}
78 changes: 78 additions & 0 deletions app/components/ui/ThemeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
'use client'

import { Monitor, Moon, Sun } from 'lucide-react'
import { useTheme, type ThemeMode } from '../../contexts/ThemeContext'

const options: { mode: ThemeMode; Icon: typeof Sun; label: string }[] = [
{ mode: 'light', Icon: Sun, label: 'Light mode' },
{ mode: 'system', Icon: Monitor, label: 'System mode' },
{ mode: 'dark', Icon: Moon, label: 'Dark mode' },
]

interface ThemeToggleProps {
/** 'pill' = compact 3-icon pill (default for navbars). 'row' = full-width row items for sidebars. */
variant?: 'pill' | 'row'
className?: string
}

export function ThemeToggle({ variant = 'pill', className = '' }: ThemeToggleProps) {
const { themeMode, setThemeMode } = useTheme()

if (variant === 'row') {
return (
<div className={`space-y-0.5 ${className}`}>
{options.map(({ mode, Icon, label }) => {
const active = themeMode === mode
return (
<button
key={mode}
onClick={() => setThemeMode(mode)}
aria-pressed={active}
aria-label={label}
className={`flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-semibold transition-colors ${
active
? 'bg-slate-100 text-slate-950 dark:bg-slate-800 dark:text-white'
: 'text-slate-500 hover:bg-slate-100 hover:text-slate-950 dark:text-slate-400 dark:hover:bg-slate-900 dark:hover:text-white'
}`}
>
<Icon className="h-4 w-4 shrink-0" />
{label}
{active && (
<span className="ml-auto h-1.5 w-1.5 rounded-full bg-blue-500" />
)}
</button>
)
})}
</div>
)
}

// Pill variant
return (
<div
className={`inline-flex items-center rounded-lg border border-slate-200 bg-slate-100 p-0.5 dark:border-slate-700 dark:bg-slate-800 ${className}`}
role="group"
aria-label="Theme selector"
>
{options.map(({ mode, Icon, label }) => {
const active = themeMode === mode
return (
<button
key={mode}
onClick={() => setThemeMode(mode)}
aria-pressed={active}
aria-label={label}
title={label}
className={`relative flex h-7 w-7 items-center justify-center rounded-md transition-all ${
active
? 'bg-white text-slate-900 shadow-sm dark:bg-slate-700 dark:text-white'
: 'text-slate-400 hover:text-slate-600 dark:text-slate-500 dark:hover:text-slate-300'
}`}
>
<Icon className="h-3.5 w-3.5" />
</button>
)
})}
</div>
)
}
62 changes: 48 additions & 14 deletions app/contexts/ThemeContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,70 @@

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

type Theme = 'light' | 'dark';
export type ThemeMode = 'light' | 'dark' | 'system';

interface ThemeContextType {
theme: Theme;
/** The currently active rendered theme (always 'light' or 'dark') */
resolvedTheme: 'light' | 'dark';
/** The user's saved preference, including 'system' */
themeMode: ThemeMode;
setThemeMode: (mode: ThemeMode) => void;
/** Legacy: kept for backwards compatibility with components that used toggleTheme */
toggleTheme: () => void;
}

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

function getSystemTheme(): 'light' | 'dark' {
if (typeof window === 'undefined') return 'light';
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}

function applyTheme(resolved: 'light' | 'dark') {
document.documentElement.classList.toggle('dark', resolved === 'dark');
}

export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState<Theme>('light');
const [themeMode, setThemeModeState] = useState<ThemeMode>('system');
const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>('light');

// On mount: read saved preference, apply it
useEffect(() => {
// Check for saved theme preference or system preference
const savedTheme = localStorage.getItem('theme') as Theme;
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
const initialTheme = savedTheme || systemTheme;
setTheme(initialTheme);
document.documentElement.classList.toggle('dark', initialTheme === 'dark');
const saved = (localStorage.getItem('theme-mode') as ThemeMode) || 'system';
const resolved = saved === 'system' ? getSystemTheme() : saved;
setThemeModeState(saved);
setResolvedTheme(resolved);
applyTheme(resolved);
}, []);

// Listen for system preference changes when in 'system' mode
useEffect(() => {
if (themeMode !== 'system') return;
const mq = window.matchMedia('(prefers-color-scheme: dark)');
const handler = (e: MediaQueryListEvent) => {
const resolved = e.matches ? 'dark' : 'light';
setResolvedTheme(resolved);
applyTheme(resolved);
};
mq.addEventListener('change', handler);
return () => mq.removeEventListener('change', handler);
}, [themeMode]);

const setThemeMode = (mode: ThemeMode) => {
const resolved = mode === 'system' ? getSystemTheme() : mode;
setThemeModeState(mode);
setResolvedTheme(resolved);
localStorage.setItem('theme-mode', mode);
applyTheme(resolved);
};

// Legacy compat: toggle between light and dark (skips system)
const toggleTheme = () => {
const newTheme = theme === 'light' ? 'dark' : 'light';
setTheme(newTheme);
localStorage.setItem('theme', newTheme);
document.documentElement.classList.toggle('dark', newTheme === 'dark');
setThemeMode(resolvedTheme === 'light' ? 'dark' : 'light');
};

return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
<ThemeContext.Provider value={{ resolvedTheme, themeMode, setThemeMode, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
Expand Down
21 changes: 3 additions & 18 deletions app/privacy/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Metadata } from 'next'
import { ArrowLeft, Shield, Lock, Eye, FileText, Database, ShieldAlert } from 'lucide-react'
import Link from 'next/link'
import { Shield, Lock, Eye, FileText, Database, ShieldAlert } from 'lucide-react'
import { LegalNav } from '../components/ui/LegalNav'
import Footer from '../components/landing/Footer'

export const metadata: Metadata = {
Expand Down Expand Up @@ -82,22 +82,7 @@ const sections = [
export default function PrivacyPolicy() {
return (
<div className="min-h-screen bg-stone-50 text-slate-950 dark:bg-slate-950 dark:text-slate-50 flex flex-col">
{/* Sticky Header */}
<header className="sticky top-0 z-50 border-b border-slate-200 bg-white/90 backdrop-blur-md dark:border-slate-800 dark:bg-slate-950/90">
<div className="mx-auto flex max-w-5xl items-center justify-between px-4 py-3 sm:px-6 lg:px-8">
<Link href="/" className="flex items-center gap-2.5 group">
<img src="/logo.png" alt="DumpIt" className="h-8 w-8 object-contain" />
<span className="text-base font-bold text-slate-900 dark:text-white">DumpIt</span>
</Link>
<Link
href="/"
className="inline-flex items-center gap-1.5 rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm font-medium text-slate-700 shadow-sm transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-200 dark:hover:bg-slate-800"
>
<ArrowLeft className="h-3.5 w-3.5" />
Back to Home
</Link>
</div>
</header>
<LegalNav />

{/* Hero */}
<section className="border-b border-slate-200 bg-gradient-to-b from-white to-stone-50 py-10 dark:border-slate-800 dark:from-slate-900 dark:to-slate-950">
Expand Down
21 changes: 3 additions & 18 deletions app/terms/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Metadata } from 'next'
import { ArrowLeft, Scale, ShieldAlert, Key, UserCheck, AlertTriangle, HelpCircle } from 'lucide-react'
import Link from 'next/link'
import { Scale, ShieldAlert, Key, UserCheck, AlertTriangle, HelpCircle } from 'lucide-react'
import { LegalNav } from '../components/ui/LegalNav'
import Footer from '../components/landing/Footer'

export const metadata: Metadata = {
Expand Down Expand Up @@ -79,22 +79,7 @@ const sections = [
export default function TermsOfService() {
return (
<div className="min-h-screen bg-stone-50 text-slate-950 dark:bg-slate-950 dark:text-slate-50 flex flex-col">
{/* Sticky Header */}
<header className="sticky top-0 z-50 border-b border-slate-200 bg-white/90 backdrop-blur-md dark:border-slate-800 dark:bg-slate-950/90">
<div className="mx-auto flex max-w-5xl items-center justify-between px-4 py-3 sm:px-6 lg:px-8">
<Link href="/" className="flex items-center gap-2.5">
<img src="/logo.png" alt="DumpIt" className="h-8 w-8 object-contain" />
<span className="text-base font-bold text-slate-900 dark:text-white">DumpIt</span>
</Link>
<Link
href="/"
className="inline-flex items-center gap-1.5 rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm font-medium text-slate-700 shadow-sm transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-200 dark:hover:bg-slate-800"
>
<ArrowLeft className="h-3.5 w-3.5" />
Back to Home
</Link>
</div>
</header>
<LegalNav />

{/* Hero */}
<section className="border-b border-slate-200 bg-gradient-to-b from-white to-stone-50 py-10 dark:border-slate-800 dark:from-slate-900 dark:to-slate-950">
Expand Down
Loading