small adaptions

This commit is contained in:
2026-02-16 19:28:23 +01:00
parent 9206453394
commit c516a097a3
3 changed files with 85 additions and 7 deletions

View File

@@ -6,16 +6,38 @@ import {
FolderOpen, FolderOpen,
BarChart3, BarChart3,
LogOut, LogOut,
ChevronDown,
Settings,
} from "lucide-react"; } from "lucide-react";
import { useAuth } from "@/contexts/AuthContext"; import { useAuth } from "@/contexts/AuthContext";
import { useState, useRef, useEffect } from "react";
export function Navbar() { export function Navbar() {
const { user, logout } = useAuth(); const { user, logout } = useAuth();
const [managementOpen, setManagementOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const navItems = [ // Close dropdown when clicking outside
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setManagementOpen(false);
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
const mainNavItems = [
{ to: "/dashboard", label: "Dashboard", icon: Clock }, { to: "/dashboard", label: "Dashboard", icon: Clock },
{ to: "/time-entries", label: "Time Entries", icon: List }, { to: "/time-entries", label: "Time Entries", icon: List },
{ to: "/statistics", label: "Statistics", icon: BarChart3 }, { to: "/statistics", label: "Statistics", icon: BarChart3 },
];
const managementItems = [
{ to: "/clients", label: "Clients", icon: Briefcase }, { to: "/clients", label: "Clients", icon: Briefcase },
{ to: "/projects", label: "Projects", icon: FolderOpen }, { to: "/projects", label: "Projects", icon: FolderOpen },
]; ];
@@ -32,7 +54,8 @@ export function Navbar() {
</span> </span>
</div> </div>
<div className="hidden sm:ml-8 sm:flex sm:space-x-4"> <div className="hidden sm:ml-8 sm:flex sm:space-x-4">
{navItems.map((item) => ( {/* Main Navigation Items */}
{mainNavItems.map((item) => (
<NavLink <NavLink
key={item.to} key={item.to}
to={item.to} to={item.to}
@@ -48,6 +71,46 @@ export function Navbar() {
{item.label} {item.label}
</NavLink> </NavLink>
))} ))}
{/* Management Dropdown */}
<div className="relative" ref={dropdownRef}>
<button
onClick={() => setManagementOpen(!managementOpen)}
className={`inline-flex h-full items-center px-3 py-2 text-sm font-medium rounded-md transition-colors ${
managementOpen
? "text-primary-600 bg-primary-50"
: "text-gray-600 hover:text-gray-900 hover:bg-gray-50"
}`}
>
<Settings className="h-4 w-4 mr-2" />
<span>Management</span>
<ChevronDown
className={`h-4 w-4 ml-1 transition-transform ${managementOpen ? "rotate-180" : ""}`}
/>
</button>
{managementOpen && (
<div className="absolute top-full left-0 mt-1 w-48 bg-white rounded-lg shadow-lg border border-gray-200 py-1 z-50">
{managementItems.map((item) => (
<NavLink
key={item.to}
to={item.to}
onClick={() => setManagementOpen(false)}
className={({ isActive }) =>
`flex items-center px-4 py-2 text-sm transition-colors ${
isActive
? "bg-primary-50 text-primary-600"
: "text-gray-700 hover:bg-gray-50"
}`
}
>
<item.icon className="h-4 w-4 mr-2" />
{item.label}
</NavLink>
))}
</div>
)}
</div>
</div> </div>
</div> </div>
<div className="flex items-center space-x-4"> <div className="flex items-center space-x-4">
@@ -64,15 +127,17 @@ export function Navbar() {
</div> </div>
</div> </div>
</div> </div>
{/* Mobile navigation */} {/* Mobile navigation */}
<div className="sm:hidden border-t border-gray-200"> <div className="sm:hidden border-t border-gray-200">
<div className="flex justify-around py-2"> <div className="flex flex-wrap">
{navItems.map((item) => ( {/* Mobile: Show all nav items directly (no dropdown) */}
{[...mainNavItems, ...managementItems].map((item) => (
<NavLink <NavLink
key={item.to} key={item.to}
to={item.to} to={item.to}
className={({ isActive }) => className={({ isActive }) =>
`flex flex-col items-center p-2 text-xs font-medium rounded-md ${ `flex-1 min-w-[80px] flex flex-col items-center p-2 text-xs font-medium ${
isActive ? "text-primary-600" : "text-gray-600" isActive ? "text-primary-600" : "text-gray-600"
}` }`
} }

View File

@@ -4,7 +4,7 @@ import { useTimeEntries } from "@/hooks/useTimeEntries";
import { import {
formatDate, formatDate,
formatDurationFromDates, formatDurationFromDates,
formatDuration, formatDurationHoursMinutes,
} from "@/utils/dateUtils"; } from "@/utils/dateUtils";
import { startOfDay, endOfDay } from "date-fns"; import { startOfDay, endOfDay } from "date-fns";
@@ -40,7 +40,7 @@ export function DashboardPage() {
<StatCard <StatCard
icon={Clock} icon={Clock}
label="Today" label="Today"
value={formatDuration(totalTodaySeconds)} value={formatDurationHoursMinutes(totalTodaySeconds)}
color="blue" color="blue"
/> />
<StatCard <StatCard

View File

@@ -30,6 +30,19 @@ export function formatDuration(totalSeconds: number): string {
return parts.join(":"); return parts.join(":");
} }
export function formatDurationHoursMinutes(totalSeconds: number): string {
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
if (hours === 0) {
return `${minutes}m`;
}
if (minutes === 0) {
return `${hours}h`;
}
return `${hours}h ${minutes}m`;
}
export function calculateDuration(startTime: string, endTime: string): number { export function calculateDuration(startTime: string, endTime: string): number {
const start = parseISO(startTime); const start = parseISO(startTime);
const end = parseISO(endTime); const end = parseISO(endTime);