diff --git a/src/app/page.tsx b/src/app/page.tsx index 21bcefd..3a1f99e 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,11 +1,21 @@ 'use client'; -import { Header, SearchBar, ServiceCard, BookmarkCard, CategorySection } from '@/components'; +import { useState } from 'react'; +import { Header, SearchBar, ServiceCard, BookmarkCard, CategorySection, Icon } from '@/components'; import { usePortal } from '@/lib/PortalContext'; import { categoryLabels, categoryOrder, bookmarkCategoryLabels, bookmarkCategoryOrder, ServiceCategory, BookmarkCategory } from '@/lib/services'; +type TabId = 'services' | 'bookmarks' | 'settings'; + +const tabs: { id: TabId; label: string; icon: string }[] = [ + { id: 'services', label: 'Services', icon: 'server' }, + { id: 'bookmarks', label: 'Bookmarks', icon: 'external-link' }, + { id: 'settings', label: 'Settings', icon: 'settings' }, +]; + export default function Home() { - const { filteredServices, filteredBookmarks, healthStatus, searchQuery } = usePortal(); + const [activeTab, setActiveTab] = useState('services'); + const { filteredServices, filteredBookmarks, healthStatus, searchQuery, darkMode, setDarkMode, services } = usePortal(); // Group services by category const servicesByCategory = categoryOrder.reduce((acc, category) => { @@ -29,73 +39,182 @@ export default function Home() { const hasBookmarks = Object.keys(bookmarksByCategory).length > 0; const noResults = searchQuery && !hasServices && !hasBookmarks; + // Count running services + const runningCount = services.filter(s => healthStatus[s.name] === 'running').length; + const totalServices = services.length; + + const renderTabContent = () => { + switch (activeTab) { + case 'services': + return ( + <> + {/* Search */} +
+ +
+ + {/* Status summary */} +
+
+ + + {runningCount} of {totalServices} services running + +
+
+ + {/* No results message */} + {noResults && ( +
+

+ No services found for "{searchQuery}" +

+
+ )} + + {/* Services */} + {hasServices && ( +
+ {Object.entries(servicesByCategory).map(([category, services]) => ( + + {services.map(service => ( + + ))} + + ))} +
+ )} + + ); + + case 'bookmarks': + return ( + <> + {/* Search */} +
+ +
+ + {/* No results message */} + {searchQuery && !hasBookmarks && ( +
+

+ No bookmarks found for "{searchQuery}" +

+
+ )} + + {/* Bookmarks */} + {hasBookmarks && ( +
+ {Object.entries(bookmarksByCategory).map(([category, bookmarks]) => ( + + {bookmarks.map(bookmark => ( + + ))} + + ))} +
+ )} + + ); + + case 'settings': + return ( +
+
+

+ Appearance +

+ + {/* Dark Mode Toggle */} +
+
+

Dark Mode

+

+ Use dark theme for the portal +

+
+ +
+ +

+ About +

+ +
+
+ Version + 1.0.0 +
+
+ Server IP + 192.168.1.3 +
+
+ Services + {totalServices} +
+
+ Bookmarks + {filteredBookmarks.length} +
+
+ +
+ + + View on Gitea + +
+
+
+ ); + + default: + return null; + } + }; + return (
-
+
setActiveTab(tab as TabId)} tabs={tabs} />
- {/* Search */} -
- -
- - {/* No results message */} - {noResults && ( -
-

- No results found for "{searchQuery}" -

-
- )} - - {/* Services */} - {hasServices && ( -
-

- Services -

- {Object.entries(servicesByCategory).map(([category, services]) => ( - - {services.map(service => ( - - ))} - - ))} -
- )} - - {/* Bookmarks */} - {hasBookmarks && ( -
-

- Bookmarks -

- {Object.entries(bookmarksByCategory).map(([category, bookmarks]) => ( - - {bookmarks.map(bookmark => ( - - ))} - - ))} -
- )} + {renderTabContent()}
{/* Footer */} diff --git a/src/components/Header.tsx b/src/components/Header.tsx index d0e64b1..ccf4cab 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -3,56 +3,89 @@ import { usePortal } from '@/lib/PortalContext'; import { Icon } from './Icons'; -export function Header() { +interface Tab { + id: string; + label: string; + icon: string; +} + +interface HeaderProps { + activeTab: string; + onTabChange: (tab: string) => void; + tabs: Tab[]; +} + +export function Header({ activeTab, onTabChange, tabs }: HeaderProps) { const { darkMode, setDarkMode, refreshHealth, isRefreshing } = usePortal(); return ( -
-
- {/* Logo / Title */} -
-
- +
+
+ {/* Top bar */} +
+ {/* Logo / Title */} +
+
+ +
+
+

+ NUC Portal +

+

+ 192.168.1.3 +

+
-
-

- NUC Portal -

-

- 192.168.1.3 -

+ + {/* Actions */} +
+ {/* Refresh button */} + + + {/* Dark mode toggle */} +
- {/* Actions */} -
- {/* Refresh button */} - - - {/* Dark mode toggle */} - -
+ {/* Tabs */} +
); diff --git a/src/components/Icons.tsx b/src/components/Icons.tsx index e6e0823..cfdd30c 100644 --- a/src/components/Icons.tsx +++ b/src/components/Icons.tsx @@ -106,6 +106,7 @@ export const icons: Record> = { 'external-link': createIcon(''), 'refresh-cw': createIcon(''), 'x': createIcon(''), + 'settings': createIcon(''), 'loader': createIcon(''), };