// App.tsx — Material Design 3 styled prototype for MotoVaultPro (MUI v5)
// Install deps (npm):
// npm install @mui/material @emotion/react @emotion/styled @mui/icons-material
// This file is TypeScript-friendly (tsx) but also works in plain React projects if the tooling supports TS.
import * as React from "react";
import { useEffect, useMemo, useState } from "react";
import { ThemeProvider, createTheme, alpha } from "@mui/material/styles";
import CssBaseline from "@mui/material/CssBaseline"; // <-- FIX: import from @mui/material, not styles
import {
Box,
Container,
Typography,
Card,
CardContent,
CardActionArea,
Button,
Grid,
Divider,
BottomNavigation,
BottomNavigationAction,
Select,
MenuItem,
FormControl,
InputLabel,
TextField,
ToggleButtonGroup,
ToggleButton,
} from "@mui/material";
import HomeRoundedIcon from "@mui/icons-material/HomeRounded";
import DirectionsCarRoundedIcon from "@mui/icons-material/DirectionsCarRounded";
import LocalGasStationRoundedIcon from "@mui/icons-material/LocalGasStationRounded";
import SettingsRoundedIcon from "@mui/icons-material/SettingsRounded";
// ---- Theme (Material Design 3-inspired) ----
const primaryHex = "#7A212A"; // brand color
const theme = 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
{
// TS note: We define a custom variant name, which runtime supports via props match,
// but TypeScript doesn't know it by default. We handle that at usage with `as any`.
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 },
},
},
},
},
});
// ---- Types ----
export type Vehicle = {
id: number;
year: number;
make: string;
model: string;
image?: string;
};
const vehiclesSeed: Vehicle[] = [
{ id: 1, year: 2021, make: "Chevrolet", model: "Malibu" },
{ id: 2, year: 2019, make: "Toyota", model: "Camry" },
{ id: 3, year: 2018, make: "Ford", model: "F-150" },
{ id: 4, year: 2022, make: "Honda", model: "CR‑V" },
];
// ---- Reusable bits ----
const Spark = ({
points = [3, 5, 4, 6, 5, 7, 6] as number[],
color = primaryHex,
}) => {
const width = 120;
const height = 36;
const max = Math.max(...points);
const min = Math.min(...points);
const path = points
.map((v, i) => {
const x = (i / (points.length - 1)) * width;
const y = height - ((v - min) / (max - min || 1)) * height;
return `${i === 0 ? "M" : "L"}${x},${y}`;
})
.join(" ");
return (
);
};
const VehicleCard = ({
v,
onClick,
}: {
v: Vehicle;
onClick?: (v: Vehicle) => void;
}) => (
onClick?.(v)}>
{v.make} {v.model}
{v.year}
);
// ---- Screens ----
const Dashboard = ({ recent }: { recent: Vehicle[] }) => (
Dashboard
Recent Vehicles
{recent.map((v) => (
))}
Fuel Spent This Month
$134.22
Average Price
$3.69/gal
);
const Vehicles = ({
vehicles,
onOpen,
}: {
vehicles: Vehicle[];
onOpen: (v: Vehicle) => void;
}) => (
Vehicles
{vehicles.map((v) => (
))}
);
const VehicleDetail = ({ v, onBack }: { v: Vehicle; onBack: () => void }) => (
{v.make} {v.model}
Fuel Logs
Apr 24
15,126 mi
Mar 13
14,300 mi
Jan 10
14,055 mi
);
const LogFuel = ({ vehicles }: { vehicles: Vehicle[] }) => {
const [vehicleId, setVehicleId] = useState(vehicles[0]?.id || 1);
const [date, setDate] = useState(new Date().toISOString().slice(0, 10));
const [odo, setOdo] = useState(15126);
const [qty, setQty] = useState(12.5);
const [price, setPrice] = useState(3.79);
const handleSave = () => {
alert(`Saved fuel log for vehicle ${vehicleId}`);
};
return (
Log Fuel
Vehicle
setDate(e.target.value)}
InputLabelProps={{ shrink: true }}
/>
setOdo(Number(e.target.value))}
/>
setQty(Number(e.target.value))}
/>
setPrice(Number(e.target.value))}
/>
);
};
const Settings = () => {
const [distance, setDistance] = useState("mi");
const [fuel, setFuel] = useState("gal");
return (
Settings
Distance
v && setDistance(v)}
sx={{ mb: 3 }}
>
Miles
Kilometers
Fuel
v && setFuel(v)}
>
U.S. Gallons
Liters
);
};
// ---- (Dev) Runtime smoke tests ----
const DevTests = () => {
useEffect(() => {
console.assert(!!ThemeProvider, "ThemeProvider should be available");
console.assert(typeof createTheme === "function", "createTheme should be a function");
console.assert(!!CssBaseline, "CssBaseline should be importable from @mui/material");
console.log("[DevTests] Basic runtime checks passed.");
}, []);
return null;
};
// ---- Root App with bottom nav ----
export default function App() {
const [tab, setTab] = useState(0); // 0: Dashboard, 1: Vehicles, 2: Log Fuel, 3: Settings
const [open, setOpen] = useState(null);
const recent = useMemo(() => vehiclesSeed.slice(0, 2), []);
return (
{tab === 0 && }
{tab === 1 &&
(open ? (
setOpen(null)} />
) : (
))}
{tab === 2 && }
{tab === 3 && }
setTab(v)}
sx={{ position: "sticky", bottom: 0, mt: 2 }}
>
} />
} />
} />
} />
);
}