mirror of
https://github.com/ManInDark/HabitTrove.git
synced 2026-01-21 06:34:30 +01:00
Added improved loading screen (#148)
This commit is contained in:
@@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Version 0.2.18
|
||||||
|
|
||||||
|
### Improved
|
||||||
|
|
||||||
|
* nicer loading UI (#147)
|
||||||
|
* header and navigation code refactor
|
||||||
|
|
||||||
## Version 0.2.17
|
## Version 0.2.17
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import './globals.css'
|
|||||||
import { Inter } from 'next/font/google'
|
import { Inter } from 'next/font/google'
|
||||||
import { DM_Sans } from 'next/font/google'
|
import { DM_Sans } from 'next/font/google'
|
||||||
import { JotaiProvider } from '@/components/jotai-providers'
|
import { JotaiProvider } from '@/components/jotai-providers'
|
||||||
import { Suspense } from 'react'
|
|
||||||
import { JotaiHydrate } from '@/components/jotai-hydrate'
|
import { JotaiHydrate } from '@/components/jotai-hydrate'
|
||||||
import { loadSettings, loadHabitsData, loadCoinsData, loadWishlistData, loadUsersData, loadServerSettings } from './actions/data'
|
import { loadSettings, loadHabitsData, loadCoinsData, loadWishlistData, loadUsersData, loadServerSettings } from './actions/data'
|
||||||
import Layout from '@/components/Layout'
|
import Layout from '@/components/Layout'
|
||||||
@@ -11,6 +10,8 @@ import { ThemeProvider } from "@/components/theme-provider"
|
|||||||
import { SessionProvider } from 'next-auth/react'
|
import { SessionProvider } from 'next-auth/react'
|
||||||
import { NextIntlClientProvider } from 'next-intl';
|
import { NextIntlClientProvider } from 'next-intl';
|
||||||
import { getLocale, getMessages } from 'next-intl/server';
|
import { getLocale, getMessages } from 'next-intl/server';
|
||||||
|
import { Suspense } from 'react'
|
||||||
|
import LoadingSpinner from '@/components/LoadingSpinner'
|
||||||
|
|
||||||
|
|
||||||
// Inter (clean, modern, excellent readability)
|
// Inter (clean, modern, excellent readability)
|
||||||
@@ -75,7 +76,7 @@ export default async function RootLayout({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<JotaiProvider>
|
<JotaiProvider>
|
||||||
<Suspense fallback="loading">
|
<Suspense fallback={<LoadingSpinner />}>
|
||||||
<JotaiHydrate
|
<JotaiHydrate
|
||||||
initialValues={{
|
initialValues={{
|
||||||
settings: initialSettings,
|
settings: initialSettings,
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { ReactNode, useEffect } from 'react'
|
import { ReactNode, Suspense, useEffect, useState } from 'react'
|
||||||
import { useAtom } from 'jotai'
|
import { useAtom } from 'jotai'
|
||||||
import { aboutOpenAtom, pomodoroAtom, userSelectAtom } from '@/lib/atoms'
|
import { aboutOpenAtom, pomodoroAtom, userSelectAtom } from '@/lib/atoms'
|
||||||
import PomodoroTimer from './PomodoroTimer'
|
import PomodoroTimer from './PomodoroTimer'
|
||||||
import UserSelectModal from './UserSelectModal'
|
import UserSelectModal from './UserSelectModal'
|
||||||
import { useSession } from 'next-auth/react'
|
import { useSession } from 'next-auth/react'
|
||||||
import AboutModal from './AboutModal'
|
import AboutModal from './AboutModal'
|
||||||
|
import LoadingSpinner from './LoadingSpinner'
|
||||||
|
|
||||||
export default function ClientWrapper({ children }: { children: ReactNode }) {
|
export default function ClientWrapper({ children }: { children: ReactNode }) {
|
||||||
const [pomo] = useAtom(pomodoroAtom)
|
const [pomo] = useAtom(pomodoroAtom)
|
||||||
@@ -14,6 +15,12 @@ export default function ClientWrapper({ children }: { children: ReactNode }) {
|
|||||||
const [aboutOpen, setAboutOpen] = useAtom(aboutOpenAtom)
|
const [aboutOpen, setAboutOpen] = useAtom(aboutOpenAtom)
|
||||||
const { data: session, status } = useSession()
|
const { data: session, status } = useSession()
|
||||||
const currentUserId = session?.user.id
|
const currentUserId = session?.user.id
|
||||||
|
const [isMounted, setIsMounted] = useState(false);
|
||||||
|
|
||||||
|
// block client-side hydration until mounted (this is crucial to wait for all jotai atoms to load), to prevent SSR hydration errors in the children components
|
||||||
|
useEffect(() => {
|
||||||
|
setIsMounted(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (status === 'loading') return
|
if (status === 'loading') return
|
||||||
@@ -22,6 +29,9 @@ export default function ClientWrapper({ children }: { children: ReactNode }) {
|
|||||||
}
|
}
|
||||||
}, [currentUserId, status, userSelect])
|
}, [currentUserId, status, userSelect])
|
||||||
|
|
||||||
|
if (!isMounted) {
|
||||||
|
return <LoadingSpinner />
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
42
components/DesktopNavDisplay.tsx
Normal file
42
components/DesktopNavDisplay.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import Link from 'next/link'
|
||||||
|
import type { ElementType } from 'react'
|
||||||
|
|
||||||
|
export interface NavItemType {
|
||||||
|
icon: ElementType;
|
||||||
|
label: string;
|
||||||
|
href: string;
|
||||||
|
position: 'main' | 'bottom';
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DesktopNavDisplayProps {
|
||||||
|
navItems: NavItemType[];
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DesktopNavDisplay({ navItems, className }: DesktopNavDisplayProps) {
|
||||||
|
// Filter for items relevant to desktop view, typically 'main' position
|
||||||
|
const desktopNavItems = navItems.filter(item => item.position === 'main');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`hidden lg:flex lg:flex-shrink-0 ${className || ''}`}>
|
||||||
|
<div className="flex flex-col w-64">
|
||||||
|
<div className="flex flex-col h-0 flex-1 bg-gray-800">
|
||||||
|
<div className="flex-1 flex flex-col pt-5 pb-4 overflow-y-auto">
|
||||||
|
<nav className="mt-5 flex-1 px-2 space-y-1">
|
||||||
|
{desktopNavItems.map((item) => (
|
||||||
|
<Link
|
||||||
|
key={item.label} // Assuming labels are unique
|
||||||
|
href={item.href}
|
||||||
|
className="group flex items-center px-2 py-2 text-sm leading-6 font-medium rounded-md text-gray-300 hover:text-white hover:bg-gray-700"
|
||||||
|
>
|
||||||
|
<item.icon className="mr-4 flex-shrink-0 h-6 w-6 text-gray-400" aria-hidden="true" />
|
||||||
|
{item.label}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,36 +1,13 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import { useAtom } from 'jotai'
|
|
||||||
import { coinsAtom, settingsAtom, browserSettingsAtom } from '@/lib/atoms'
|
|
||||||
import { useCoins } from '@/hooks/useCoins'
|
|
||||||
import { FormattedNumber } from '@/components/FormattedNumber'
|
|
||||||
import { Menu, Settings, User, Info, Coins } from 'lucide-react'
|
|
||||||
import { Button } from '@/components/ui/button'
|
|
||||||
import { Logo } from '@/components/Logo'
|
import { Logo } from '@/components/Logo'
|
||||||
import NotificationBell from './NotificationBell'
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from '@/components/ui/dropdown-menu'
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import dynamic from 'next/dynamic'
|
import HeaderActions from './HeaderActions'
|
||||||
import { Profile } from './Profile'
|
|
||||||
import { useHelpers } from '@/lib/client-helpers'
|
|
||||||
|
|
||||||
interface HeaderProps {
|
interface HeaderProps {
|
||||||
className?: string
|
className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const TodayEarnedCoins = dynamic(() => import('./TodayEarnedCoins'), { ssr: false })
|
|
||||||
|
|
||||||
export default function Header({ className }: HeaderProps) {
|
export default function Header({ className }: HeaderProps) {
|
||||||
const [settings] = useAtom(settingsAtom)
|
|
||||||
const [browserSettings] = useAtom(browserSettingsAtom)
|
|
||||||
const { balance } = useCoins()
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<header className={`border-b bg-white dark:bg-gray-800 shadow-sm ${className || ''}`}>
|
<header className={`border-b bg-white dark:bg-gray-800 shadow-sm ${className || ''}`}>
|
||||||
@@ -39,23 +16,7 @@ export default function Header({ className }: HeaderProps) {
|
|||||||
<Link href="/" className="mr-3 sm:mr-4">
|
<Link href="/" className="mr-3 sm:mr-4">
|
||||||
<Logo />
|
<Logo />
|
||||||
</Link>
|
</Link>
|
||||||
<div className="flex items-center gap-1 sm:gap-2">
|
<HeaderActions />
|
||||||
<Link href="/coins" className="flex items-center gap-1 sm:gap-2 px-3 py-1.5 bg-white hover:bg-gray-50 dark:bg-gray-700 dark:hover:bg-gray-600 rounded-full transition-colors border border-gray-200 dark:border-gray-600">
|
|
||||||
<Coins className="h-5 w-5 text-yellow-500 dark:text-yellow-400" />
|
|
||||||
<div className="flex items-baseline gap-1 sm:gap-2">
|
|
||||||
<FormattedNumber
|
|
||||||
amount={balance}
|
|
||||||
settings={settings}
|
|
||||||
className="text-gray-800 dark:text-gray-100 font-medium text-lg"
|
|
||||||
/>
|
|
||||||
<div className="hidden sm:block">
|
|
||||||
<TodayEarnedCoins />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
<NotificationBell />
|
|
||||||
<Profile />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
38
components/HeaderActions.tsx
Normal file
38
components/HeaderActions.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { useAtom } from 'jotai'
|
||||||
|
import { settingsAtom } from '@/lib/atoms'
|
||||||
|
import { useCoins } from '@/hooks/useCoins'
|
||||||
|
import { FormattedNumber } from '@/components/FormattedNumber'
|
||||||
|
import { Coins } from 'lucide-react'
|
||||||
|
import NotificationBell from './NotificationBell'
|
||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
import { Profile } from './Profile'
|
||||||
|
|
||||||
|
const TodayEarnedCoins = dynamic(() => import('./TodayEarnedCoins'), { ssr: false })
|
||||||
|
|
||||||
|
export default function HeaderActions() {
|
||||||
|
const [settings] = useAtom(settingsAtom)
|
||||||
|
const { balance } = useCoins()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-1 sm:gap-2">
|
||||||
|
<Link href="/coins" className="flex items-center gap-1 sm:gap-2 px-3 py-1.5 bg-white hover:bg-gray-50 dark:bg-gray-700 dark:hover:bg-gray-600 rounded-full transition-colors border border-gray-200 dark:border-gray-600">
|
||||||
|
<Coins className="h-5 w-5 text-yellow-500 dark:text-yellow-400" />
|
||||||
|
<div className="flex items-baseline gap-1 sm:gap-2">
|
||||||
|
<FormattedNumber
|
||||||
|
amount={balance}
|
||||||
|
settings={settings}
|
||||||
|
className="text-gray-800 dark:text-gray-100 font-medium text-lg"
|
||||||
|
/>
|
||||||
|
<div className="hidden sm:block">
|
||||||
|
<TodayEarnedCoins />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
<NotificationBell />
|
||||||
|
<Profile />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
61
components/LoadingSpinner.tsx
Normal file
61
components/LoadingSpinner.tsx
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Coins } from 'lucide-react';
|
||||||
|
import { Logo } from '@/components/Logo';
|
||||||
|
|
||||||
|
const subtexts = [
|
||||||
|
"Unearthing your treasures",
|
||||||
|
"Polishing your gems",
|
||||||
|
"Mining for good habits",
|
||||||
|
"Stumbling upon brilliance",
|
||||||
|
"Discovering your potential",
|
||||||
|
"Crafting your success story",
|
||||||
|
"Forging new paths",
|
||||||
|
"Summoning success",
|
||||||
|
"Brewing brilliance",
|
||||||
|
"Charging up your awesome",
|
||||||
|
"Assembling achievements",
|
||||||
|
"Leveling up your day",
|
||||||
|
"Questing for quality",
|
||||||
|
"Unlocking awesomeness",
|
||||||
|
"Plotting your progress",
|
||||||
|
];
|
||||||
|
|
||||||
|
const LoadingSpinner: React.FC = () => {
|
||||||
|
const [currentSubtext, setCurrentSubtext] = useState<string>('Loading your data');
|
||||||
|
const [animatedDots, setAnimatedDots] = useState<string>('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const randomIndex = Math.floor(Math.random() * subtexts.length);
|
||||||
|
setCurrentSubtext(subtexts[randomIndex]);
|
||||||
|
|
||||||
|
const dotAnimationInterval = setInterval(() => {
|
||||||
|
setAnimatedDots(prevDots => {
|
||||||
|
if (prevDots.length >= 3) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return prevDots + '.';
|
||||||
|
});
|
||||||
|
}, 200); // Adjust timing as needed
|
||||||
|
|
||||||
|
return () => clearInterval(dotAnimationInterval);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center h-screen">
|
||||||
|
<div className="flex flex-col items-center space-y-4">
|
||||||
|
<Coins className="h-12 w-12 animate-bounce text-yellow-500" />
|
||||||
|
<Logo />
|
||||||
|
{currentSubtext && (
|
||||||
|
<p className="text-lg text-gray-600 dark:text-gray-400">
|
||||||
|
{currentSubtext}{animatedDots}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LoadingSpinner;
|
||||||
45
components/MobileNavDisplay.tsx
Normal file
45
components/MobileNavDisplay.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import Link from 'next/link'
|
||||||
|
import type { ElementType } from 'react'
|
||||||
|
|
||||||
|
export interface NavItemType {
|
||||||
|
icon: ElementType;
|
||||||
|
label: string;
|
||||||
|
href: string;
|
||||||
|
position: 'main' | 'bottom';
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MobileNavDisplayProps {
|
||||||
|
navItems: NavItemType[];
|
||||||
|
isIOS: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MobileNavDisplay({ navItems, isIOS }: MobileNavDisplayProps) {
|
||||||
|
// Filter for items relevant to mobile view, typically 'main' and 'bottom' positions
|
||||||
|
const mobileNavItems = navItems.filter(item => item.position === 'main' || item.position === 'bottom');
|
||||||
|
// The original code spread main and bottom items separately, effectively concatenating them.
|
||||||
|
// If specific ordering or duplication was intended, that logic would be here.
|
||||||
|
// For now, a simple filter and map should suffice if all items are distinct.
|
||||||
|
// The original code: [...navItems(isTasksView).filter(item => item.position === 'main'), ...navItems(isTasksView).filter(item => item.position === 'bottom')]
|
||||||
|
// This implies that items could be in 'main' or 'bottom'. The current navItems only have 'main'.
|
||||||
|
// A simple combined list is fine.
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={isIOS ? "pb-20" : "pb-16"} /> {/* Add padding at the bottom to prevent content from being hidden */}
|
||||||
|
<nav className={`lg:hidden fixed bottom-0 left-0 right-0 bg-white dark:bg-gray-800 shadow-lg ${isIOS ? "pb-4" : ""}`}>
|
||||||
|
<div className="grid grid-cols-5 w-full">
|
||||||
|
{mobileNavItems.map((item) => (
|
||||||
|
<Link
|
||||||
|
key={item.label} // Assuming labels are unique
|
||||||
|
href={item.href}
|
||||||
|
className="flex flex-col items-center justify-center py-2 text-gray-600 dark:text-gray-300 hover:text-blue-500 dark:hover:text-blue-400"
|
||||||
|
>
|
||||||
|
<item.icon className="h-6 w-6" />
|
||||||
|
<span className="text-xs mt-1">{item.label}</span>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,16 +1,24 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import Link from 'next/link'
|
import { Home, Calendar, Gift, Coins } from 'lucide-react'
|
||||||
import { Home, Calendar, List, Gift, Coins, Settings, Info, CheckSquare } from 'lucide-react'
|
|
||||||
import { useAtom } from 'jotai'
|
import { useAtom } from 'jotai'
|
||||||
import { browserSettingsAtom } from '@/lib/atoms'
|
import { browserSettingsAtom } from '@/lib/atoms'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState, ElementType } from 'react'
|
||||||
import { useTranslations } from 'next-intl'
|
import { useTranslations } from 'next-intl'
|
||||||
import { HabitIcon, TaskIcon } from '@/lib/constants'
|
import { HabitIcon, TaskIcon } from '@/lib/constants'
|
||||||
import { useHelpers } from '@/lib/client-helpers'
|
import { useHelpers } from '@/lib/client-helpers'
|
||||||
|
import MobileNavDisplay from './MobileNavDisplay'
|
||||||
|
import DesktopNavDisplay from './DesktopNavDisplay'
|
||||||
|
|
||||||
type ViewPort = 'main' | 'mobile'
|
type ViewPort = 'main' | 'mobile'
|
||||||
|
|
||||||
|
export interface NavItemType {
|
||||||
|
icon: ElementType;
|
||||||
|
label: string;
|
||||||
|
href: string;
|
||||||
|
position: 'main' | 'bottom';
|
||||||
|
}
|
||||||
|
|
||||||
interface NavigationProps {
|
interface NavigationProps {
|
||||||
className?: string
|
className?: string
|
||||||
viewPort: ViewPort
|
viewPort: ViewPort
|
||||||
@@ -18,13 +26,12 @@ interface NavigationProps {
|
|||||||
|
|
||||||
export default function Navigation({ className, viewPort }: NavigationProps) {
|
export default function Navigation({ className, viewPort }: NavigationProps) {
|
||||||
const t = useTranslations('Navigation')
|
const t = useTranslations('Navigation')
|
||||||
const [showAbout, setShowAbout] = useState(false)
|
|
||||||
const [isMobileView, setIsMobileView] = useState(false)
|
const [isMobileView, setIsMobileView] = useState(false)
|
||||||
const [browserSettings] = useAtom(browserSettingsAtom)
|
const [browserSettings] = useAtom(browserSettingsAtom)
|
||||||
const isTasksView = browserSettings.viewType === 'tasks'
|
const isTasksView = browserSettings.viewType === 'tasks'
|
||||||
const { isIOS } = useHelpers()
|
const { isIOS } = useHelpers()
|
||||||
|
|
||||||
const navItems = (isTasksView: boolean) => [
|
const currentNavItems: NavItemType[] = [
|
||||||
{ icon: Home, label: t('dashboard'), href: '/', position: 'main' },
|
{ icon: Home, label: t('dashboard'), href: '/', position: 'main' },
|
||||||
{
|
{
|
||||||
icon: isTasksView ? TaskIcon : HabitIcon,
|
icon: isTasksView ? TaskIcon : HabitIcon,
|
||||||
@@ -53,49 +60,12 @@ export default function Navigation({ className, viewPort }: NavigationProps) {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
if (viewPort === 'mobile' && isMobileView) {
|
if (viewPort === 'mobile' && isMobileView) {
|
||||||
return (
|
return <MobileNavDisplay navItems={currentNavItems} isIOS={isIOS} />
|
||||||
<>
|
|
||||||
<div className={isIOS ? "pb-20" : "pb-16"} /> {/* Add padding at the bottom to prevent content from being hidden */}
|
|
||||||
<nav className={`lg:hidden fixed bottom-0 left-0 right-0 bg-white dark:bg-gray-800 shadow-lg ${isIOS ? "pb-4" : ""}`}>
|
|
||||||
<div className="grid grid-cols-5 w-full">
|
|
||||||
{[...navItems(isTasksView).filter(item => item.position === 'main'), ...navItems(isTasksView).filter(item => item.position === 'bottom')].map((item) => (
|
|
||||||
<Link
|
|
||||||
key={item.label}
|
|
||||||
href={item.href}
|
|
||||||
className="flex flex-col items-center justify-center py-2 text-gray-600 dark:text-gray-300 hover:text-blue-500 dark:hover:text-blue-400"
|
|
||||||
>
|
|
||||||
<item.icon className="h-6 w-6" />
|
|
||||||
<span className="text-xs mt-1">{item.label}</span>
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viewPort === 'main' && !isMobileView) {
|
if (viewPort === 'main' && !isMobileView) {
|
||||||
return (
|
return <DesktopNavDisplay navItems={currentNavItems} className={className} />
|
||||||
<div className="hidden lg:flex lg:flex-shrink-0">
|
|
||||||
<div className="flex flex-col w-64">
|
|
||||||
<div className="flex flex-col h-0 flex-1 bg-gray-800">
|
|
||||||
<div className="flex-1 flex flex-col pt-5 pb-4 overflow-y-auto">
|
|
||||||
<nav className="mt-5 flex-1 px-2 space-y-1">
|
|
||||||
{navItems(isTasksView).filter(item => item.position === 'main').map((item) => (
|
|
||||||
<Link
|
|
||||||
key={item.label}
|
|
||||||
href={item.href}
|
|
||||||
className="group flex items-center px-2 py-2 text-sm leading-6 font-medium rounded-md text-gray-300 hover:text-white hover:bg-gray-700"
|
|
||||||
>
|
|
||||||
<item.icon className="mr-4 flex-shrink-0 h-6 w-6 text-gray-400" aria-hidden="true" />
|
|
||||||
{item.label}
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null // Explicitly return null if no view matches
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "habittrove",
|
"name": "habittrove",
|
||||||
"version": "0.2.17",
|
"version": "0.2.18",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack",
|
"dev": "next dev --turbopack",
|
||||||
|
|||||||
Reference in New Issue
Block a user