Refactored the header

This commit is contained in:
Logan Cusano
2025-05-28 23:10:54 -04:00
parent c651c73fe3
commit 7cbbc86ee4
6 changed files with 114 additions and 48 deletions

View File

@@ -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
>
<AuthProvider>
{/* ThemeToggle placed in a fixed position */}
<div className="top-4 right-4 z-50"> {/* Add these classes for positioning */}
<ThemeToggle />
</div>
{children}
<footer>
<div className="max-w-7xl mx-auto py-8 px-4 sm:px-6 lg:px-8 flex flex-col sm:flex-row justify-between items-center text-sm text-gray-500">
<div className="mb-4 sm:mb-0 text-center sm:text-left">
&copy; 2025 Logan Cusano. All rights reserved.
<HeaderProvider> {/* Wrap with HeaderProvider */}
<Header /> {/* Header component now consumes HeaderContext */}
{children}
<footer>
<div className="max-w-7xl mx-auto py-8 px-4 sm:px-6 lg:px-8 flex flex-col sm:flex-row justify-between items-center text-sm text-gray-500">
<div className="mb-4 sm:mb-0 text-center sm:text-left">
&copy; 2025 Logan Cusano. All rights reserved.
</div>
<div className="flex space-x-6">
<a href="https://git.vpn.cusano.net/logan/drb-frontend/issues" target="_blank" rel="noopener noreferrer" className="hover:text-gray-700">Submit Issues</a>
</div>
</div>
<div className="flex space-x-6">
<a href="https://git.vpn.cusano.net/logan/drb-frontend/issues" target="_blank" rel="noopener noreferrer" className="hover:text-gray-700">Submit Issues</a>
</div>
</div>
</footer>
</footer>
</HeaderProvider>
</AuthProvider>
<Toaster />
</ThemeProvider>

View File

@@ -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 <div className="flex items-center justify-center min-h-screen bg-background text-foreground">Loading Authentication...</div>;
}
// 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 (
<div className="flex items-center justify-center min-h-screen bg-background text-foreground">
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 (
<div className="min-h-screen bg-background text-foreground font-sans">
<header className="flex justify-between items-center p-4 bg-card text-card-foreground shadow-md">
<h1 className="text-xl font-bold">Radio App Admin</h1>
</header>
<main className="p-6">
{!user ? (
<LoginPage />
@@ -56,14 +59,6 @@ const ClientDetailPage: React.FC = () => {
return (
<div className="min-h-screen bg-background text-foreground font-sans">
<header className="flex justify-between items-center p-4 bg-card text-card-foreground shadow-md">
<h1 className="text-xl font-bold">Radio App Admin</h1>
<div className="flex items-center space-x-4">
<span className="text-sm text-muted-foreground">Logged in as: {user.username} ({user.role})</span>
<Button onClick={() => router.push('/')} variant="outline">Back to Management</Button>
<Button onClick={logout} variant="outline">Logout</Button>
</div>
</header>
<main className="p-6">
<IndividualClientPage clientId={clientIdentifier} token={token} logoutUser={logout} />
</main>

View File

@@ -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 (
<AuthProvider>
<AppContent />
</AuthProvider>
<AppContent />
);
}

View File

@@ -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 (
<div className="min-h-screen bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100 font-sans">
<header className="flex justify-between items-center p-4 bg-white dark:bg-gray-800 shadow-md">
<h1 className="text-xl font-bold">Radio App Admin</h1>
<div className="flex items-center space-x-4">
<span className="text-sm">Logged in as: {user.username} ({user.role})</span>
<Button onClick={handleLogoutAndRedirect} variant="outline">Logout</Button>
</div>
</header>
{/* Header is now in layout.tsx */}
<main className="p-6">
<div className="mb-4">

39
src/components/Header.tsx Normal file
View File

@@ -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 (
<header className="flex justify-between items-center p-4 bg-card text-card-foreground shadow-md">
<div className="flex items-center">
{config.showBackButton && ( // Use config from context
<Button onClick={() => router.back()} variant="outline" className="mr-4">
Back
</Button>
)}
<h1 className="text-xl font-bold">Radio App Admin</h1>
</div>
<div className="flex items-center space-x-4">
{user && <span className="text-sm text-muted-foreground">Logged in as: {user.username} ({user.role})</span>}
{user && <Button onClick={handleLogoutAndRedirect} variant="outline">Logout</Button>}
<ThemeToggle />
</div>
</header>
);
};
export default Header;

View File

@@ -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<HeaderConfig>) => void;
}
const defaultHeaderConfig: HeaderConfig = {
showBackButton: false,
};
const HeaderContext = createContext<HeaderContextType | undefined>(undefined);
export const HeaderProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [config, setConfig] = useState<HeaderConfig>(defaultHeaderConfig);
const setHeaderConfig = useCallback((newConfig: Partial<HeaderConfig>) => {
setConfig(prevConfig => ({ ...prevConfig, ...newConfig }));
}, []);
return (
<HeaderContext.Provider value={{ config, setHeaderConfig }}>
{children}
</HeaderContext.Provider>
);
};
export const useHeader = () => {
const context = useContext(HeaderContext);
if (context === undefined) {
throw new Error('useHeader must be used within a HeaderProvider');
}
return context;
};