From 7cbbc86ee4e66923918b5c4d0b627e2f98f0be68 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Wed, 28 May 2025 23:10:54 -0400 Subject: [PATCH 1/3] Refactored the header --- src/app/layout.tsx | 31 +++++++++++------------ src/app/nodes/[clientId]/page.tsx | 35 +++++++++++--------------- src/app/page.tsx | 5 +--- src/components/AppContent.tsx | 10 ++------ src/components/Header.tsx | 39 ++++++++++++++++++++++++++++ src/context/HeaderContext.tsx | 42 +++++++++++++++++++++++++++++++ 6 files changed, 114 insertions(+), 48 deletions(-) create mode 100644 src/components/Header.tsx create mode 100644 src/context/HeaderContext.tsx diff --git a/src/app/layout.tsx b/src/app/layout.tsx index d2773a3..6dd7391 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -4,7 +4,8 @@ import { Geist, Geist_Mono } from "next/font/google"; import { Toaster } from '@/components/ui/sonner'; import { ThemeProvider } from '@/components/ThemeProvider'; import { AuthProvider } from '@/context/AuthContext'; -import { ThemeToggle } from '@/components/ThemeToggle'; // Import ThemeToggle +import { HeaderProvider } from '@/context/HeaderContext'; // Import HeaderProvider +import Header from '@/components/Header'; import "./globals.css"; const geistSans = Geist({ @@ -39,22 +40,20 @@ export default function RootLayout({ disableTransitionOnChange > - {/* ThemeToggle placed in a fixed position */} -
{/* Add these classes for positioning */} - -
- - {children} -
-
-
- © 2025 Logan Cusano. All rights reserved. + {/* Wrap with HeaderProvider */} +
{/* Header component now consumes HeaderContext */} + {children} +
-
+ +
diff --git a/src/app/nodes/[clientId]/page.tsx b/src/app/nodes/[clientId]/page.tsx index 1df2208..ada8594 100644 --- a/src/app/nodes/[clientId]/page.tsx +++ b/src/app/nodes/[clientId]/page.tsx @@ -1,30 +1,37 @@ // app/nodes/[clientId]/page.tsx "use client"; -import React from 'react'; +import React, { useEffect } from 'react'; // Import useEffect import { useParams, useRouter } from 'next/navigation'; import { useAuth } from '@/context/AuthContext'; +import { useHeader } from '@/context/HeaderContext'; // Import useHeader import IndividualClientPage from '@/components/IndividualClientPage'; import LoginPage from '@/components/LoginPage'; import { UserRoles } from '@/types'; -import { Button } from '@/components/ui/button'; const ClientDetailPage: React.FC = () => { const router = useRouter(); - const params = useParams(); // params can be null or an object with string | string[] values - + const params = useParams(); const { user, loading, token, hasPermission, logout } = useAuth(); + const { setHeaderConfig } = useHeader(); + + // Use useEffect to set header configuration on mount and clean up on unmount + useEffect(() => { + setHeaderConfig({ showBackButton: true }); + + return () => { + setHeaderConfig({ showBackButton: false }); + }; + }, [setHeaderConfig]); // Dependency array: ensure effect runs if setHeaderConfig changes (unlikely) + if (loading) { return
Loading Authentication...
; } - // Safely extract clientId, handling the case where params might be null or clientId might be undefined - const clientId = params?.clientId; // Use optional chaining + const clientId = params?.clientId; if (!clientId) { - // This covers cases where params is null, or clientId property is missing/undefined - // or if the URL param isn't properly captured. return (
Client ID not found in URL. @@ -32,15 +39,11 @@ const ClientDetailPage: React.FC = () => { ); } - // Ensure clientIdentifier is a string (use the first element if it's an array) const clientIdentifier = Array.isArray(clientId) ? clientId[0] : clientId; if (!user || !token || !hasPermission(UserRoles.MOD)) { return (
-
-

Radio App Admin

-
{!user ? ( @@ -56,14 +59,6 @@ const ClientDetailPage: React.FC = () => { return (
-
-

Radio App Admin

-
- Logged in as: {user.username} ({user.role}) - - -
-
diff --git a/src/app/page.tsx b/src/app/page.tsx index 120b3f6..8845792 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,12 +1,9 @@ "use client"; import React from 'react'; -import { AuthProvider } from '@/context/AuthContext'; import AppContent from '@/components/AppContent'; export default function App() { return ( - - - + ); } \ No newline at end of file diff --git a/src/components/AppContent.tsx b/src/components/AppContent.tsx index 5c2998d..3234544 100644 --- a/src/components/AppContent.tsx +++ b/src/components/AppContent.tsx @@ -2,7 +2,7 @@ "use client"; import React, { useState, useEffect } from 'react'; import { Button } from '@/components/ui/button'; -import { useAuth } from '@/context/AuthContext'; // Correct path to useAuth +import { useAuth } from '@/context/AuthContext'; import LoginPage from '@/components/LoginPage'; import BotsManagement from '@/components/BotsManagement'; import SystemsManagement from '@/components/SystemsManagement'; @@ -45,13 +45,7 @@ const AppContent: React.FC = () => { // If loading is false, and we have a user with MOD permission and a token, render the main app content return (
-
-

Radio App Admin

-
- Logged in as: {user.username} ({user.role}) - -
-
+ {/* Header is now in layout.tsx */}
diff --git a/src/components/Header.tsx b/src/components/Header.tsx new file mode 100644 index 0000000..5f0500e --- /dev/null +++ b/src/components/Header.tsx @@ -0,0 +1,39 @@ +// components/Header.tsx +"use client"; +import React from 'react'; +import { useRouter } from 'next/navigation'; +import { Button } from '@/components/ui/button'; +import { useAuth } from '@/context/AuthContext'; +import { useHeader } from '@/context/HeaderContext'; // Import useHeader hook +import { ThemeToggle } from '@/components/ThemeToggle'; + +const Header: React.FC = () => { + const router = useRouter(); + const { user, logout } = useAuth(); + const { config } = useHeader(); // Consume the header config from context + + const handleLogoutAndRedirect = () => { + logout(); + router.push('/'); + }; + + return ( +
+
+ {config.showBackButton && ( // Use config from context + + )} +

Radio App Admin

+
+
+ {user && Logged in as: {user.username} ({user.role})} + {user && } + +
+
+ ); +}; + +export default Header; \ No newline at end of file diff --git a/src/context/HeaderContext.tsx b/src/context/HeaderContext.tsx new file mode 100644 index 0000000..c725464 --- /dev/null +++ b/src/context/HeaderContext.tsx @@ -0,0 +1,42 @@ +// context/HeaderContext.tsx +"use client"; + +import React, { createContext, useContext, useState, ReactNode, useCallback } from 'react'; + +interface HeaderConfig { + showBackButton: boolean; + // Add other configurable items here, e.g., title, custom elements +} + +interface HeaderContextType { + config: HeaderConfig; + setHeaderConfig: (newConfig: Partial) => void; +} + +const defaultHeaderConfig: HeaderConfig = { + showBackButton: false, +}; + +const HeaderContext = createContext(undefined); + +export const HeaderProvider: React.FC<{ children: ReactNode }> = ({ children }) => { + const [config, setConfig] = useState(defaultHeaderConfig); + + const setHeaderConfig = useCallback((newConfig: Partial) => { + setConfig(prevConfig => ({ ...prevConfig, ...newConfig })); + }, []); + + return ( + + {children} + + ); +}; + +export const useHeader = () => { + const context = useContext(HeaderContext); + if (context === undefined) { + throw new Error('useHeader must be used within a HeaderProvider'); + } + return context; +}; \ No newline at end of file -- 2.49.1 From bd8c9fde8b03d7e0c023972897b681ae114777ec Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Wed, 28 May 2025 23:13:38 -0400 Subject: [PATCH 2/3] Enforced the theme on the main page --- src/components/AppContent.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/AppContent.tsx b/src/components/AppContent.tsx index 3234544..b463df4 100644 --- a/src/components/AppContent.tsx +++ b/src/components/AppContent.tsx @@ -21,7 +21,8 @@ const AppContent: React.FC = () => { // Display a loading indicator while AuthContext is determining authentication status if (loading) { - return
Loading Authentication...
; + // Changed this line to use bg-background + return
Loading Authentication...
; } // Once loading is false, if no user is authenticated, display the LoginPage @@ -32,7 +33,8 @@ const AppContent: React.FC = () => { // If a user is authenticated but lacks the required permission, display an access denied message if (user && !hasPermission(UserRoles.MOD)) { return ( -
+ // Changed this line to use bg-background +

Access Denied

You do not have sufficient permissions to view this page. Your role: {user.role}. Required: {UserRoles.MOD}. @@ -44,7 +46,8 @@ const AppContent: React.FC = () => { // If loading is false, and we have a user with MOD permission and a token, render the main app content return ( -

+ // Changed this line to use bg-background +
{/* Header is now in layout.tsx */}
-- 2.49.1 From f8154e22ae12ef9793a4aae0c278095ecf3a9349 Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Wed, 28 May 2025 23:16:18 -0400 Subject: [PATCH 3/3] Update theme colors --- src/app/globals.css | 69 +++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index 64ed405..991a1e4 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,3 +1,4 @@ +/* globals.css */ @import "tailwindcss"; @import "tw-animate-css"; @@ -45,7 +46,7 @@ :root { --radius: 0.625rem; - /* Light Mode Palette */ + /* Light Mode Palette - Retained similar modern, soft feel */ --background: oklch(0.99 0 0); /* Very light off-white */ --foreground: oklch(0.1 0 0); /* Very dark gray */ --card: oklch(0.99 0 0); @@ -84,42 +85,42 @@ } .dark { - /* Dark Mode Palette */ - --background: oklch(0.12 0 0); /* Very dark charcoal */ - --foreground: oklch(0.92 0 0); /* Light gray */ - --card: oklch(0.18 0 0); /* Slightly lighter dark for cards */ - --card-foreground: oklch(0.92 0 0); - --popover: oklch(0.18 0 0); - --popover-foreground: oklch(0.92 0 0); - --primary: oklch(0.7 0.15 260); /* Brighter, more vibrant indigo for dark mode */ - --primary-foreground: oklch(0.15 0 0); /* Dark gray for text on primary */ - --secondary: oklch(0.22 0 0); /* Darker gray */ - --secondary-foreground: oklch(0.85 0 0); /* Lighter gray */ - --muted: oklch(0.22 0 0); - --muted-foreground: oklch(0.6 0 0); /* Mid-gray */ - --accent: oklch(0.22 0.03 260); /* Subtle dark hint of primary */ - --accent-foreground: oklch(0.8 0.05 260); /* Lighter desaturated primary */ - --destructive: oklch(0.7 0.15 20); /* Brighter red for dark mode */ - --border: oklch(0.25 0 0); /* Medium dark gray */ - --input: oklch(0.2 0 0); /* Darker than border */ - --ring: oklch(0.4 0 0); /* Mid-dark gray */ + /* Dark Mode Palette - Modernized */ + --background: oklch(0.18 0 0); /* Slightly lighter dark charcoal */ + --foreground: oklch(0.88 0 0); /* Soft white/light gray */ + --card: oklch(0.22 0 0); /* Slightly lighter for cards than background */ + --card-foreground: oklch(0.88 0 0); + --popover: oklch(0.22 0 0); + --popover-foreground: oklch(0.88 0 0); + --primary: oklch(0.65 0.18 260); /* Brighter, more vibrant indigo */ + --primary-foreground: oklch(0.18 0 0); /* Darker text on primary */ + --secondary: oklch(0.28 0 0); /* Medium dark gray */ + --secondary-foreground: oklch(0.75 0 0); /* Lighter gray */ + --muted: oklch(0.28 0 0); + --muted-foreground: oklch(0.55 0 0); /* Mid-gray */ + --accent: oklch(0.28 0.04 260); /* Subtle dark hint of primary */ + --accent-foreground: oklch(0.7 0.06 260); /* Lighter desaturated primary */ + --destructive: oklch(0.65 0.16 20); /* Brighter red */ + --border: oklch(0.32 0 0); /* Medium dark gray */ + --input: oklch(0.28 0 0); /* Darker than border */ + --ring: oklch(0.5 0 0); /* Mid-dark gray */ - /* Chart Colors (Dark Mode) - Brighter and distinct */ - --chart-1: oklch(0.7 0.18 270); /* Brighter purple-blue */ - --chart-2: oklch(0.75 0.15 180); /* Brighter teal */ - --chart-3: oklch(0.8 0.12 90); /* Brighter muted green-yellow */ - --chart-4: oklch(0.75 0.16 30); /* Brighter orange-brown */ - --chart-5: oklch(0.85 0.1 330); /* Brighter muted pink */ + /* Chart Colors (Dark Mode) - Brighter and distinct for visibility */ + --chart-1: oklch(0.75 0.2 270); /* Brighter purple-blue */ + --chart-2: oklch(0.8 0.18 180); /* Brighter teal */ + --chart-3: oklch(0.85 0.15 90); /* Brighter muted green-yellow */ + --chart-4: oklch(0.8 0.18 30); /* Brighter orange-brown */ + --chart-5: oklch(0.9 0.12 330); /* Brighter muted pink */ /* Sidebar Colors (Dark Mode) */ - --sidebar: oklch(0.18 0 0); /* Slightly lighter dark for sidebar */ - --sidebar-foreground: oklch(0.92 0 0); - --sidebar-primary: oklch(0.7 0.15 260); - --sidebar-primary-foreground: oklch(0.15 0 0); - --sidebar-accent: oklch(0.22 0.03 260); - --sidebar-accent-foreground: oklch(0.8 0.05 260); - --sidebar-border: oklch(0.25 0 0); - --sidebar-ring: oklch(0.4 0 0); + --sidebar: oklch(0.22 0 0); /* Slightly lighter dark for sidebar */ + --sidebar-foreground: oklch(0.88 0 0); + --sidebar-primary: oklch(0.65 0.18 260); + --sidebar-primary-foreground: oklch(0.18 0 0); + --sidebar-accent: oklch(0.28 0.04 260); + --sidebar-accent-foreground: oklch(0.7 0.06 260); + --sidebar-border: oklch(0.32 0 0); + --sidebar-ring: oklch(0.5 0 0); } @layer base { -- 2.49.1