UI/UX improvements: Premium redesign with navigation restructure, glassmorphism stat cards, and background integration

This commit is contained in:
2025-10-29 19:18:44 +05:30
parent 77f8197fc1
commit 6b5ccf3123
57 changed files with 10536 additions and 520 deletions

View File

@@ -1,99 +1,217 @@
import React from 'react';
import React, { useMemo } from 'react';
import type { CSSProperties } from 'react';
import { theme } from '../theme/index';
import icLauncher from '../assets/ic_launcher_foreground.png';
type Item = {
export interface SidebarNavItem {
id: string;
label: string;
icon: React.ReactNode;
};
interface SidebarNavProps {
items: Item[];
activeId: string;
onSelect: (id: string) => void;
badge?: number;
}
const SidebarNav: React.FC<SidebarNavProps> = ({ items, activeId, onSelect }) => {
interface SidebarNavProps {
items: SidebarNavItem[];
footerItems?: SidebarNavItem[];
activeId: string;
collapsed?: boolean;
onSelect: (id: string) => void;
onToggleCollapse?: (collapsed: boolean) => void;
variant?: 'desktop' | 'mobile';
}
const SidebarNav: React.FC<SidebarNavProps> = ({
items,
footerItems,
activeId,
collapsed = false,
onSelect,
onToggleCollapse,
variant = 'desktop',
}) => {
const containerStyles: CSSProperties = useMemo(
() => ({
width: collapsed ? '92px' : '280px',
transition: 'width 240ms ease',
background: 'linear-gradient(180deg, rgba(4, 24, 21, 0.98) 0%, rgba(6, 35, 31, 0.95) 100%)',
borderRight: variant === 'desktop' ? `1px solid rgba(203, 169, 81, 0.15)` : undefined,
padding: collapsed ? '28px 12px 24px' : '28px 20px 24px',
display: 'flex',
flexDirection: 'column',
gap: theme.spacing.md,
height: variant === 'desktop' ? '100vh' : '100%',
position: variant === 'desktop' ? 'sticky' : 'relative',
top: variant === 'desktop' ? 0 : undefined,
backdropFilter: 'blur(16px)',
boxShadow: variant === 'mobile' ? theme.shadows.xl : 'none',
zIndex: variant === 'mobile' ? theme.zIndex.modal : theme.zIndex.sticky,
}),
[collapsed, variant],
);
const headerStyles: CSSProperties = useMemo(
() => ({
display: 'flex',
alignItems: 'center',
gap: theme.spacing.md,
paddingBottom: theme.spacing.lg,
borderBottom: `1px solid rgba(203, 169, 81, 0.12)`,
}),
[],
);
const logoStyles: CSSProperties = {
width: '48px',
height: '48px',
borderRadius: theme.borderRadius.sm,
backgroundColor: 'rgba(203, 169, 81, 0.12)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
boxShadow: 'inset 0 0 0 1px rgba(203, 169, 81, 0.2)',
};
const logoTextStyles: CSSProperties = {
...theme.typography.h4,
color: theme.colors.textPrimary,
};
const navListStyles: CSSProperties = useMemo(
() => ({
display: 'flex',
flexDirection: 'column',
gap: '2px',
flex: 1,
overflowY: 'auto',
paddingRight: collapsed ? 0 : '8px',
marginTop: theme.spacing.sm,
}),
[collapsed],
);
const footerStyles: CSSProperties = {
marginTop: 'auto',
paddingTop: theme.spacing.lg,
display: 'flex',
flexDirection: 'column',
gap: '2px',
};
const dividerStyles: CSSProperties = {
height: '1px',
background: 'linear-gradient(90deg, transparent 0%, rgba(203, 169, 81, 0.3) 50%, transparent 100%)',
marginBottom: theme.spacing.sm,
};
return (
<aside className="w-16 md:w-56 border-r border-luxury-gold/20 bg-gradient-to-b from-deep-forest/90 via-deep-forest/80 to-rich-brown/70 backdrop-blur-xl sticky top-16 md:top-20 h-[calc(100vh-4rem)] md:h-[calc(100vh-5rem)] overflow-hidden">
{/* Decorative background elements */}
<div className="absolute inset-0 pointer-events-none">
<div className="absolute -top-20 -left-10 w-40 h-40 bg-luxury-gold/5 rounded-full blur-3xl"></div>
<div className="absolute -bottom-20 -right-10 w-32 h-32 bg-bronze/5 rounded-full blur-2xl"></div>
<div className="absolute top-1/2 left-0 w-0.5 h-32 bg-gradient-to-b from-transparent via-luxury-gold/20 to-transparent"></div>
</div>
{/* Premium border accent */}
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-transparent via-luxury-gold/40 to-transparent"></div>
<nav className="relative p-2 md:p-4 space-y-1.5">
{/* Navigation title */}
<div className="hidden md:block mb-3">
<h3 className="text-luxury-gold/70 text-[10px] font-montserrat font-medium uppercase tracking-wider mb-1">Navigation</h3>
<div className="w-6 h-0.5 bg-gradient-to-r from-luxury-gold to-transparent rounded-full"></div>
<aside style={containerStyles}>
<div style={headerStyles}>
<div style={logoStyles}>
<img
src={icLauncher}
alt="Pennyzo"
style={{ width: '60%', height: '60%', objectFit: 'contain' }}
/>
</div>
{items.map((it, index) => {
const isActive = activeId === it.id;
{!collapsed && <span style={logoTextStyles}>Pennyzo</span>}
{variant === 'desktop' && onToggleCollapse && (
<button
onClick={() => onToggleCollapse(!collapsed)}
aria-label={collapsed ? 'Expand navigation' : 'Collapse navigation'}
style={{
marginLeft: 'auto',
width: '36px',
height: '36px',
borderRadius: theme.borderRadius.md,
border: `1px solid rgba(203, 169, 81, 0.25)`,
backgroundColor: 'rgba(203, 169, 81, 0.08)',
color: theme.colors.highlight,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
transition: 'all 180ms ease',
fontSize: '18px',
fontWeight: 'bold',
}}
>
{collapsed ? '»' : '«'}
</button>
)}
</div>
{/* Decorative divider - works in both collapsed and expanded states */}
<div style={{
height: '1px',
background: 'linear-gradient(90deg, transparent 0%, rgba(203, 169, 81, 0.3) 50%, transparent 100%)',
marginTop: theme.spacing.md,
marginBottom: theme.spacing.sm,
}} />
<nav
style={navListStyles}
className="custom-scrollbar pennyzo-scroll"
>
{items.map((item) => {
const isActive = activeId === item.id;
return (
<button
key={it.id}
onClick={() => onSelect(it.id)}
className={`group relative w-full flex items-center justify-start gap-2 md:gap-2.5 px-3 py-2.5 rounded-lg transition-colors duration-300 ${
isActive
? 'bg-gradient-to-r from-luxury-gold/20 to-brass/20 border border-luxury-gold/50 text-cream shadow-[0_2px_8px_rgba(197,157,89,0.15)]'
: 'border border-transparent hover:border-luxury-gold/35 hover:bg-gradient-to-r hover:from-luxury-gold/10 hover:to-transparent text-cream/80 hover:text-cream'
}`}
style={{
animationDelay: `${index * 100}ms`
}}
key={item.id}
type="button"
onClick={() => onSelect(item.id)}
className={`nav-item ${isActive ? 'nav-item-active' : 'nav-item-inactive'} pennyzo-transition`}
aria-label={item.label}
title={collapsed ? item.label : undefined}
>
{/* Icon container with enhanced styling */}
<div className={`relative w-8 h-8 rounded-md flex items-center justify-center transition-all duration-300 ${
isActive
? 'bg-gradient-to-br from-luxury-gold/40 to-brass/40 ring-1 ring-luxury-gold/40'
: 'bg-deep-forest/60 group-hover:bg-luxury-gold/20'
}`}>
<span className={`text-lg transition-all duration-300 ${
isActive ? 'scale-110' : 'group-hover:scale-105'
}`}>
{it.icon}
</span>
{/* Active indicator dot */}
{isActive && (
<div className="absolute -right-1 -top-1 w-3 h-3 bg-luxury-gold rounded-full shadow-[0_0_8px_rgba(197,157,89,0.6)] animate-pulse"></div>
)}
{isActive && <div className="nav-item-indicator" />}
<div className={`nav-icon ${isActive ? 'nav-icon-active' : 'nav-icon-inactive'}`}>
{item.icon}
</div>
{/* Label with enhanced typography */}
<div className="hidden md:flex flex-1 min-w-0 items-center">
<span className={`truncate text-sm md:text-base leading-snug font-montserrat font-medium text-left tracking-wide transition-colors duration-300 ${
isActive ? 'text-cream' : 'text-cream/85 group-hover:text-cream'
}`}>
{it.label}
</span>
{/* Subtle underline for active item */}
{isActive && (
<div className="absolute -bottom-1 left-0 w-5 h-0.5 bg-gradient-to-r from-luxury-gold/70 to-transparent rounded-full"></div>
)}
</div>
{/* Hover effect overlay */}
<div className="absolute inset-0 rounded-lg bg-gradient-to-r from-luxury-gold/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"></div>
{/* Active selection indicator */}
{isActive && (
<div className="absolute left-0 top-1/2 -translate-y-1/2 w-0.5 h-6 bg-gradient-to-b from-luxury-gold to-brass rounded-r-full shadow-[0_0_6px_rgba(197,157,89,0.45)]"></div>
{!collapsed && (
<div style={{ flex: 1, display: 'flex', alignItems: 'center', gap: theme.spacing.sm }}>
<span className={`nav-label ${isActive ? 'nav-label-active' : 'nav-label-inactive'}`}>
{item.label}
</span>
{item.badge && item.badge > 0 && (
<span className="pennyzo-badge">{item.badge > 99 ? '99+' : item.badge}</span>
)}
</div>
)}
</button>
);
})}
{/* Bottom decorative element */}
{/* Removed large decorative bottom element for compactness */}
</nav>
{/* Bottom gradient fade */}
<div className="absolute bottom-0 left-0 w-full h-16 bg-gradient-to-t from-deep-forest/50 to-transparent pointer-events-none"></div>
{footerItems && footerItems.length > 0 && (
<div style={footerStyles}>
{/* Footer divider */}
<div style={dividerStyles} />
{footerItems.map((item) => {
const isActive = activeId === item.id;
return (
<button
key={item.id}
type="button"
onClick={() => onSelect(item.id)}
className={`nav-item ${isActive ? 'nav-item-active' : 'nav-item-inactive'} pennyzo-transition`}
aria-label={item.label}
title={collapsed ? item.label : undefined}
>
{isActive && <div className="nav-item-indicator" />}
<div className={`nav-icon ${isActive ? 'nav-icon-active' : 'nav-icon-inactive'}`}>
{item.icon}
</div>
{!collapsed && (
<span className={`nav-label ${isActive ? 'nav-label-active' : 'nav-label-inactive'}`}>
{item.label}
</span>
)}
</button>
);
})}
</div>
)}
</aside>
);
};