feat: navigation and UX improvements complete

This commit is contained in:
Eric Gullickson
2025-12-26 09:25:42 -06:00
parent 50baec390f
commit 8c13dc0a55
23 changed files with 327 additions and 126 deletions

View File

@@ -49,38 +49,38 @@ export const SignupForm: React.FC<SignupFormProps> = ({ onSubmit, loading }) =>
return (
<form onSubmit={handleSubmit(handleFormSubmit)} className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Email Address <span className="text-red-500">*</span>
<label className="block text-sm font-medium text-avus mb-1">
Email Address <span className="text-red-400">*</span>
</label>
<input
{...register('email')}
type="email"
inputMode="email"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 min-h-[44px]"
className="w-full px-3 py-2 border rounded-md min-h-[44px] bg-scuro text-avus border-silverstone placeholder-canna focus:outline-none focus:ring-2 focus:ring-abudhabi focus:border-abudhabi"
placeholder="your.email@example.com"
style={{ fontSize: '16px' }}
/>
{errors.email && (
<p className="mt-1 text-sm text-red-600">{errors.email.message}</p>
<p className="mt-1 text-sm text-red-400">{errors.email.message}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Password <span className="text-red-500">*</span>
<label className="block text-sm font-medium text-avus mb-1">
Password <span className="text-red-400">*</span>
</label>
<div className="relative">
<input
{...register('password')}
type={showPassword ? 'text' : 'password'}
className="w-full px-3 py-2 pr-10 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 min-h-[44px]"
className="w-full px-3 py-2 pr-10 border rounded-md min-h-[44px] bg-scuro text-avus border-silverstone placeholder-canna focus:outline-none focus:ring-2 focus:ring-abudhabi focus:border-abudhabi"
placeholder="At least 8 characters"
style={{ fontSize: '16px' }}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700 focus:outline-none min-w-[44px] min-h-[44px] flex items-center justify-center"
className="absolute right-3 top-1/2 -translate-y-1/2 text-titanio hover:text-avus focus:outline-none min-w-[44px] min-h-[44px] flex items-center justify-center"
aria-label={showPassword ? 'Hide password' : 'Show password'}
>
{showPassword ? (
@@ -96,29 +96,29 @@ export const SignupForm: React.FC<SignupFormProps> = ({ onSubmit, loading }) =>
</button>
</div>
{errors.password && (
<p className="mt-1 text-sm text-red-600">{errors.password.message}</p>
<p className="mt-1 text-sm text-red-400">{errors.password.message}</p>
)}
<p className="mt-1 text-xs text-gray-600">
<p className="mt-1 text-xs text-titanio">
Must be at least 8 characters with one uppercase letter and one number
</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Confirm Password <span className="text-red-500">*</span>
<label className="block text-sm font-medium text-avus mb-1">
Confirm Password <span className="text-red-400">*</span>
</label>
<div className="relative">
<input
{...register('confirmPassword')}
type={showConfirmPassword ? 'text' : 'password'}
className="w-full px-3 py-2 pr-10 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 min-h-[44px]"
className="w-full px-3 py-2 pr-10 border rounded-md min-h-[44px] bg-scuro text-avus border-silverstone placeholder-canna focus:outline-none focus:ring-2 focus:ring-abudhabi focus:border-abudhabi"
placeholder="Re-enter your password"
style={{ fontSize: '16px' }}
/>
<button
type="button"
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700 focus:outline-none min-w-[44px] min-h-[44px] flex items-center justify-center"
className="absolute right-3 top-1/2 -translate-y-1/2 text-titanio hover:text-avus focus:outline-none min-w-[44px] min-h-[44px] flex items-center justify-center"
aria-label={showConfirmPassword ? 'Hide password' : 'Show password'}
>
{showConfirmPassword ? (
@@ -134,7 +134,7 @@ export const SignupForm: React.FC<SignupFormProps> = ({ onSubmit, loading }) =>
</button>
</div>
{errors.confirmPassword && (
<p className="mt-1 text-sm text-red-600">{errors.confirmPassword.message}</p>
<p className="mt-1 text-sm text-red-400">{errors.confirmPassword.message}</p>
)}
</div>

View File

@@ -25,24 +25,28 @@ export const SignupPage: React.FC = () => {
};
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-rose-50 flex items-center justify-center p-4">
<div className="min-h-screen bg-nero flex items-center justify-center p-4">
<div className="w-full max-w-md">
<div className="bg-white rounded-lg shadow-lg p-8">
<div className="bg-nero border border-white/10 rounded-lg shadow-lg shadow-black/30 p-8">
<div className="text-center mb-8">
<h1 className="text-3xl font-bold text-primary-600 mb-2">MotoVaultPro</h1>
<h2 className="text-xl font-semibold text-gray-800">Create Your Account</h2>
<p className="text-sm text-gray-600 mt-2">
<img
src="/images/logos/motovaultpro-title-slogan.png"
alt="MotoVaultPro - Precision Vehicle Management"
className="h-12 md:h-14 w-auto mx-auto mb-4"
/>
<h2 className="text-xl font-semibold text-avus">Create Your Account</h2>
<p className="text-sm text-titanio mt-2">
Start tracking your vehicle maintenance and fuel logs
</p>
</div>
<SignupForm onSubmit={handleSubmit} loading={isPending} />
<div className="mt-6 text-center text-sm text-gray-600">
<div className="mt-6 text-center text-sm text-titanio">
Already have an account?{' '}
<button
onClick={() => navigate('/login')}
className="text-primary-600 hover:text-primary-700 font-medium focus:outline-none focus:underline"
className="text-primary-500 hover:text-primary-400 font-medium focus:outline-none focus:underline"
>
Login
</button>

View File

@@ -158,7 +158,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
<form onSubmit={handleSubmit} className="w-full">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">Vehicle</label>
<label className="text-sm font-medium text-slate-700 dark:text-avus mb-1">Vehicle</label>
<select
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
value={vehicleID}
@@ -173,7 +173,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
</div>
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">Document Type</label>
<label className="text-sm font-medium text-slate-700 dark:text-avus mb-1">Document Type</label>
<select
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
value={documentType}
@@ -186,7 +186,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
</div>
<div className="flex flex-col md:col-span-2">
<label className="text-sm font-medium text-slate-700 mb-1">Title</label>
<label className="text-sm font-medium text-slate-700 dark:text-avus mb-1">Title</label>
<input
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:placeholder-canna dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="text"
@@ -204,7 +204,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
{documentType === 'insurance' && (
<>
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">Insurance company</label>
<label className="text-sm font-medium text-slate-700 dark:text-avus mb-1">Insurance company</label>
<input
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="text"
@@ -213,7 +213,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
/>
</div>
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">Policy number</label>
<label className="text-sm font-medium text-slate-700 dark:text-avus mb-1">Policy number</label>
<input
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="text"
@@ -260,7 +260,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
</div>
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">Bodily Injury (Person)</label>
<label className="text-sm font-medium text-slate-700 dark:text-avus mb-1">Bodily Injury (Person)</label>
<input
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:placeholder-canna dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="text"
@@ -270,7 +270,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
/>
</div>
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">Bodily Injury (Incident)</label>
<label className="text-sm font-medium text-slate-700 dark:text-avus mb-1">Bodily Injury (Incident)</label>
<input
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:placeholder-canna dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="text"
@@ -281,7 +281,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
</div>
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">Property Damage</label>
<label className="text-sm font-medium text-slate-700 dark:text-avus mb-1">Property Damage</label>
<input
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:placeholder-canna dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="text"
@@ -291,7 +291,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
/>
</div>
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">Premium</label>
<label className="text-sm font-medium text-slate-700 dark:text-avus mb-1">Premium</label>
<input
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:placeholder-canna dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="number"
@@ -307,7 +307,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
{documentType === 'registration' && (
<>
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">License Plate</label>
<label className="text-sm font-medium text-slate-700 dark:text-avus mb-1">License Plate</label>
<input
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="text"
@@ -334,7 +334,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
/>
</div>
<div className="flex flex-col md:col-span-2">
<label className="text-sm font-medium text-slate-700 mb-1">Cost</label>
<label className="text-sm font-medium text-slate-700 dark:text-avus mb-1">Cost</label>
<input
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:placeholder-canna dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="number"
@@ -356,16 +356,16 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
onChange={(e) => setScanForMaintenance(e.target.checked)}
className="w-5 h-5 rounded border-slate-300 text-primary-600 focus:ring-primary-500 dark:border-silverstone dark:focus:ring-abudhabi"
/>
<span className="ml-2 text-sm font-medium text-slate-700">
<span className="ml-2 text-sm font-medium text-slate-700 dark:text-avus">
Scan for Maintenance Schedule
</span>
</label>
<span className="ml-2 text-xs text-slate-500">(Coming soon)</span>
<span className="ml-2 text-xs text-slate-500 dark:text-titanio">(Coming soon)</span>
</div>
)}
<div className="flex flex-col md:col-span-2">
<label className="text-sm font-medium text-slate-700 mb-1">Notes</label>
<label className="text-sm font-medium text-slate-700 dark:text-avus mb-1">Notes</label>
<textarea
className="min-h-[88px] rounded-lg border px-3 py-2 bg-white text-gray-900 border-slate-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
value={notes}
@@ -374,7 +374,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
</div>
<div className="flex flex-col md:col-span-2">
<label className="text-sm font-medium text-slate-700 mb-1">Upload image/PDF</label>
<label className="text-sm font-medium text-slate-700 dark:text-avus mb-1">Upload image/PDF</label>
<input
className="h-11 min-h-[44px] rounded-lg border px-3 py-2 bg-white text-gray-900 border-slate-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-medium file:bg-primary-500/10 file:text-primary-600 dark:file:bg-abudhabi/20 dark:file:text-abudhabi"
type="file"
@@ -382,13 +382,13 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
onChange={(e) => setFile(e.target.files?.[0] || null)}
/>
{uploadProgress > 0 && uploadProgress < 100 && (
<div className="text-sm text-slate-600 mt-1">Uploading... {uploadProgress}%</div>
<div className="text-sm text-slate-600 dark:text-titanio mt-1">Uploading... {uploadProgress}%</div>
)}
</div>
</div>
{error && (
<div className="text-red-600 text-sm mt-3">{error}</div>
<div className="text-red-600 dark:text-red-400 text-sm mt-3">{error}</div>
)}
<div className="flex flex-col sm:flex-row gap-2 mt-4">

View File

@@ -187,12 +187,13 @@ const FuelLogFormComponent: React.FC<{ onSuccess?: () => void; initial?: Partial
fullWidth
InputProps={{
readOnly: true,
sx: {
backgroundColor: 'grey.50',
sx: (theme) => ({
backgroundColor: theme.palette.mode === 'dark' ? '#4C4E4D' : 'grey.50',
'& .MuiOutlinedInput-input': {
cursor: 'default',
color: theme.palette.mode === 'dark' ? '#F2F3F6' : 'inherit',
},
},
}),
}}
helperText="Calculated from distance ÷ fuel amount"
sx={{

View File

@@ -330,10 +330,10 @@ export const StationPicker: React.FC<StationPickerProps> = ({
}}
/>
)}
sx={{
sx={(theme) => ({
'& .MuiAutocomplete-groupLabel': {
fontWeight: 600,
backgroundColor: 'grey.100',
backgroundColor: theme.palette.mode === 'dark' ? '#4C4E4D' : 'grey.100',
fontSize: '0.75rem',
textTransform: 'uppercase',
letterSpacing: '0.5px'
@@ -342,7 +342,7 @@ export const StationPicker: React.FC<StationPickerProps> = ({
minHeight: '44px', // Mobile touch target
padding: '8px 16px'
}
}}
})}
/>
);
};

View File

@@ -11,6 +11,7 @@ export interface UserPreferences {
unitSystem: UnitSystem;
currencyCode: string;
timeZone: string;
darkMode: boolean | null;
createdAt: string;
updatedAt: string;
}
@@ -19,4 +20,5 @@ export interface UpdatePreferencesRequest {
unitSystem?: UnitSystem;
currencyCode?: string;
timeZone?: string;
darkMode?: boolean | null;
}

View File

@@ -396,7 +396,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div className="mb-6">
<label className="block text-sm font-medium text-gray-700 mb-2">
<label className="block text-sm font-medium text-gray-700 dark:text-avus mb-2">
Vehicle Photo
</label>
<VehicleImageUpload
@@ -408,10 +408,10 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-gray-700 dark:text-avus mb-1">
VIN or License Plate <span className="text-red-500">*</span>
</label>
<p className="text-xs text-gray-600 mb-2">
<p className="text-xs text-gray-600 dark:text-titanio mb-2">
Enter vehicle VIN (optional)
</p>
<input
@@ -421,14 +421,14 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
style={{ fontSize: '16px' }}
/>
{errors.vin && (
<p className="mt-1 text-sm text-red-600">{errors.vin.message}</p>
<p className="mt-1 text-sm text-red-600 dark:text-red-400">{errors.vin.message}</p>
)}
</div>
{/* Vehicle Specification Dropdowns */}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-gray-700 dark:text-avus mb-1">
Year
</label>
<select
@@ -451,7 +451,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-gray-700 dark:text-avus mb-1">
Make
</label>
<select
@@ -483,7 +483,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-gray-700 dark:text-avus mb-1">
Model
</label>
<select
@@ -518,7 +518,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
{/* Trim (left) */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-gray-700 dark:text-avus mb-1">
Trim
</label>
<select
@@ -551,7 +551,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
{/* Engine (middle) */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-gray-700 dark:text-avus mb-1">
Engine
</label>
<select
@@ -579,7 +579,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
{/* Transmission (right) */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-gray-700 dark:text-avus mb-1">
Transmission
</label>
<select
@@ -607,7 +607,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-gray-700 dark:text-avus mb-1">
Nickname
</label>
<input
@@ -620,7 +620,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-gray-700 dark:text-avus mb-1">
Color
</label>
<input
@@ -632,7 +632,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-gray-700 dark:text-avus mb-1">
License Plate
</label>
<input
@@ -642,13 +642,13 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
style={{ fontSize: '16px' }}
/>
{errors.licensePlate && (
<p className="mt-1 text-sm text-red-600">{errors.licensePlate.message}</p>
<p className="mt-1 text-sm text-red-600 dark:text-red-400">{errors.licensePlate.message}</p>
)}
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-gray-700 dark:text-avus mb-1">
Current Odometer Reading
</label>
<input

View File

@@ -29,12 +29,12 @@ const DetailField: React.FC<{
className?: string;
}> = ({ label, value, isRequired, className = "" }) => (
<div className={`space-y-1 ${className}`}>
<label className="block text-sm font-medium text-gray-700">
<label className="block text-sm font-medium text-gray-700 dark:text-avus">
{label} {isRequired && <span className="text-red-500">*</span>}
</label>
<div className="px-3 py-2 bg-gray-50 border border-gray-200 rounded-md">
<span className="text-gray-900">
{value || <span className="text-gray-400 italic">Not provided</span>}
<div className="px-3 py-2 bg-gray-50 dark:bg-scuro border border-gray-200 dark:border-silverstone rounded-md">
<span className="text-gray-900 dark:text-avus">
{value || <span className="text-gray-400 dark:text-titanio italic">Not provided</span>}
</span>
</div>
</div>