MVP with new UX
This commit is contained in:
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
41
frontend/src/shared-minimal/components/mobile/GlassCard.tsx
Normal file
41
frontend/src/shared-minimal/components/mobile/GlassCard.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
40
frontend/src/shared-minimal/components/mobile/MobilePill.tsx
Normal file
40
frontend/src/shared-minimal/components/mobile/MobilePill.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
96
frontend/src/shared-minimal/theme/md3Theme.ts
Normal file
96
frontend/src/shared-minimal/theme/md3Theme.ts
Normal 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
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user