MVP with new UX

This commit is contained in:
Eric Gullickson
2025-08-09 17:45:54 -05:00
parent 8f5117a4e2
commit d60c3ec00e
18 changed files with 1572 additions and 573 deletions

View File

@@ -1,9 +1,9 @@
/**
* @ai-summary Reusable card component
* @ai-summary Reusable card component with Material Design 3
*/
import React from 'react';
import { clsx } from 'clsx';
import { Card as MuiCard, CardContent } from '@mui/material';
interface CardProps {
children: React.ReactNode;
@@ -18,24 +18,27 @@ export const Card: React.FC<CardProps> = ({
padding = 'md',
onClick,
}) => {
const paddings = {
none: '',
sm: 'p-3',
md: 'p-4',
lg: 'p-6',
const paddingStyles = {
none: 0,
sm: 1,
md: 2,
lg: 3,
};
return (
<div
className={clsx(
'bg-white rounded-lg shadow-sm border border-gray-200',
paddings[padding],
onClick && 'cursor-pointer',
className
)}
<MuiCard
onClick={onClick}
sx={{
cursor: onClick ? 'pointer' : 'default',
'&:hover': onClick ? {
boxShadow: 2
} : {}
}}
className={className}
>
{children}
</div>
<CardContent sx={{ p: paddingStyles[padding] }}>
{children}
</CardContent>
</MuiCard>
);
};

View File

@@ -0,0 +1,50 @@
/**
* @ai-summary Bottom navigation component with Material Design 3
*/
import React from 'react';
import { BottomNavigation as MuiBottomNavigation, BottomNavigationAction } from '@mui/material';
export interface NavigationItem {
key: string;
label: string;
icon: React.ReactNode;
}
interface BottomNavigationProps {
items: NavigationItem[];
activeItem: string;
onItemSelect: (key: string) => void;
}
export const BottomNavigation: React.FC<BottomNavigationProps> = ({
items,
activeItem,
onItemSelect
}) => {
const activeIndex = items.findIndex(item => item.key === activeItem);
return (
<MuiBottomNavigation
showLabels
value={activeIndex}
onChange={(_, newValue) => onItemSelect(items[newValue].key)}
sx={{
position: 'fixed',
bottom: 0,
left: 0,
right: 0,
zIndex: 1000,
width: '100%'
}}
>
{items.map(({ key, label, icon }) => (
<BottomNavigationAction
key={key}
label={label}
icon={icon}
/>
))}
</MuiBottomNavigation>
);
};

View File

@@ -0,0 +1,41 @@
/**
* @ai-summary Glass morphism card component for mobile UI
*/
import React from 'react';
import { clsx } from 'clsx';
interface GlassCardProps {
children: React.ReactNode;
className?: string;
padding?: 'none' | 'sm' | 'md' | 'lg';
onClick?: () => void;
}
export const GlassCard: React.FC<GlassCardProps> = ({
children,
className,
padding = 'md',
onClick,
}) => {
const paddings = {
none: '',
sm: 'p-3',
md: 'p-4',
lg: 'p-6',
};
return (
<div
className={clsx(
'rounded-3xl border border-slate-200/70 bg-white/80 shadow-sm backdrop-blur',
paddings[padding],
onClick && 'cursor-pointer hover:shadow-xl hover:-translate-y-0.5 transition',
className
)}
onClick={onClick}
>
{children}
</div>
);
};

View File

@@ -0,0 +1,23 @@
/**
* @ai-summary Mobile app container with glass morphism styling
*/
import React from 'react';
interface MobileContainerProps {
children: React.ReactNode;
className?: string;
}
export const MobileContainer: React.FC<MobileContainerProps> = ({
children,
className = ''
}) => {
return (
<div className="w-full min-h-screen bg-gradient-to-br from-slate-50 via-white to-rose-50 flex items-start justify-center p-4 md:py-6">
<div className={`w-full max-w-[380px] min-h-screen md:min-h-[600px] md:rounded-[32px] shadow-2xl flex flex-col border-0 md:border border-slate-200/70 bg-white/90 md:bg-white/70 backdrop-blur-xl ${className}`}>
{children}
</div>
</div>
);
};

View File

@@ -0,0 +1,40 @@
/**
* @ai-summary Mobile pill button component with gradient styling
*/
import React from 'react';
import { clsx } from 'clsx';
// Theme colors now defined in Tailwind config
interface MobilePillProps {
active?: boolean;
label: string;
onClick?: () => void;
icon?: React.ReactNode;
className?: string;
}
export const MobilePill: React.FC<MobilePillProps> = ({
active = false,
label,
onClick,
icon,
className
}) => {
return (
<button
onClick={onClick}
className={clsx(
"group h-11 rounded-2xl text-sm font-medium border transition flex items-center justify-center gap-2 backdrop-blur",
active
? "text-white border-transparent shadow-lg bg-gradient-moto"
: "bg-white/80 text-slate-800 border-slate-200 hover:bg-slate-50",
className
)}
>
{icon}
<span>{label}</span>
</button>
);
};

View File

@@ -0,0 +1,96 @@
/**
* @ai-summary Material Design 3 theme configuration for MotoVaultPro
*/
import { createTheme, alpha } from '@mui/material/styles';
// Brand color from mockup
const primaryHex = '#7A212A';
export const md3Theme = createTheme({
palette: {
mode: 'light',
primary: {
main: primaryHex
},
secondary: {
main: alpha(primaryHex, 0.8)
},
background: {
default: '#F8F5F3',
paper: '#FFFFFF'
},
},
shape: {
borderRadius: 16
},
typography: {
fontFamily:
'Inter, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif',
h3: {
fontWeight: 700,
letterSpacing: -0.5
},
},
components: {
MuiCard: {
styleOverrides: {
root: {
borderRadius: 20,
boxShadow:
'0 1px 2px rgba(16,24,40,.04), 0 4px 16px rgba(16,24,40,.06)',
},
},
},
MuiButton: {
variants: [
// Custom MD3-like "tonal" button
{
props: { variant: 'tonal' as any },
style: ({ theme }) => ({
backgroundColor: alpha(theme.palette.primary.main, 0.12),
color: theme.palette.primary.main,
borderRadius: 999,
textTransform: 'none',
fontWeight: 600,
paddingInline: 18,
'&:hover': {
backgroundColor: alpha(theme.palette.primary.main, 0.18)
},
}),
},
{
props: { variant: 'contained' },
style: ({ theme }) => ({
borderRadius: 999,
textTransform: 'none',
fontWeight: 700,
paddingInline: 20,
boxShadow: '0 8px 24px ' + alpha(theme.palette.primary.main, 0.25),
}),
},
],
},
MuiBottomNavigation: {
styleOverrides: {
root: {
borderTop: '1px solid rgba(16,24,40,.08)',
background: alpha('#FFFFFF', 0.8),
backdropFilter: 'blur(8px)',
},
},
},
MuiBottomNavigationAction: {
styleOverrides: {
root: {
minWidth: 0,
paddingTop: 8,
paddingBottom: 8,
'&.Mui-selected': {
color: primaryHex
},
},
},
},
},
});