Files
motovaultpro/docs/changes/mobile-optimization-v1/03-MOBILE-SETTINGS.md
Eric Gullickson a052040e3a Initial Commit
2025-09-17 16:09:15 -05:00

14 KiB

Mobile Settings Implementation Guide

Overview

Complete implementation guide for creating a full-featured mobile settings screen that matches desktop functionality. This addresses the critical gap where desktop has comprehensive settings but mobile only has a placeholder.

Current State Analysis

Desktop Settings (Full Implementation)

File: /home/egullickson/motovaultpro/frontend/src/pages/SettingsPage.tsx

Features:

  • Account management section
  • Notifications settings
  • Appearance & Units (dark mode, metric/imperial)
  • Data export and management
  • Account actions (logout, delete account)

Mobile Settings (Placeholder Only)

File: frontend/src/App.tsx (lines 113-122)

Current Implementation:

const SettingsScreen = () => (
  <div className="space-y-4">
    <GlassCard>
      <div className="text-center py-12">
        <h2 className="text-lg font-semibold text-slate-800 mb-2">Settings</h2>
        <p className="text-slate-500">Coming soon - App settings and preferences</p>
      </div>
    </GlassCard>
  </div>
);

Implementation Strategy

Step 1: Create Mobile Settings Directory Structure

Create dedicated mobile settings components following existing patterns:

frontend/src/features/settings/
├── mobile/
│   ├── MobileSettingsScreen.tsx      # Main settings screen
│   ├── AccountSection.tsx           # Account management
│   ├── NotificationsSection.tsx     # Notification preferences
│   ├── AppearanceSection.tsx        # Dark mode & units
│   ├── DataSection.tsx              # Export & data management
│   └── AccountActionsSection.tsx    # Logout & delete account
└── hooks/
    ├── useSettings.ts               # Settings state management
    └── useSettingsPersistence.ts    # Settings persistence

Step 2: Implement Mobile Settings Screen Component

File: frontend/src/features/settings/mobile/MobileSettingsScreen.tsx

import React from 'react';
import { GlassCard, MobileContainer } from '../../../shared-minimal/components/mobile';
import { AccountSection } from './AccountSection';
import { NotificationsSection } from './NotificationsSection';
import { AppearanceSection } from './AppearanceSection';
import { DataSection } from './DataSection';
import { AccountActionsSection } from './AccountActionsSection';

export const MobileSettingsScreen: React.FC = () => {
  return (
    <MobileContainer>
      <div className="space-y-4 pb-20"> {/* Bottom padding for nav */}
        <div className="text-center mb-6">
          <h1 className="text-2xl font-bold text-slate-800">Settings</h1>
          <p className="text-slate-500 mt-2">Manage your account and preferences</p>
        </div>

        <AccountSection />
        <NotificationsSection />
        <AppearanceSection />
        <DataSection />
        <AccountActionsSection />
      </div>
    </MobileContainer>
  );
};

Step 3: Implement Settings Sections

Account Section Component

File: frontend/src/features/settings/mobile/AccountSection.tsx

import React from 'react';
import { useAuth0 } from '@auth0/auth0-react';
import { GlassCard } from '../../../shared-minimal/components/mobile';

export const AccountSection: React.FC = () => {
  const { user } = useAuth0();

  return (
    <GlassCard>
      <div className="p-4">
        <h2 className="text-lg font-semibold text-slate-800 mb-4">Account</h2>

        <div className="space-y-3">
          <div className="flex items-center space-x-3">
            <img
              src={user?.picture}
              alt="Profile"
              className="w-12 h-12 rounded-full"
            />
            <div>
              <p className="font-medium text-slate-800">{user?.name}</p>
              <p className="text-sm text-slate-500">{user?.email}</p>
            </div>
          </div>

          <div className="pt-2 border-t border-slate-200">
            <p className="text-sm text-slate-600">
              Member since {new Date(user?.updated_at || '').toLocaleDateString()}
            </p>
          </div>
        </div>
      </div>
    </GlassCard>
  );
};

Appearance Section Component

File: frontend/src/features/settings/mobile/AppearanceSection.tsx

import React from 'react';
import { GlassCard } from '../../../shared-minimal/components/mobile';
import { useSettings } from '../hooks/useSettings';

export const AppearanceSection: React.FC = () => {
  const { settings, updateSetting } = useSettings();

  const toggleDarkMode = () => {
    updateSetting('darkMode', !settings.darkMode);
  };

  const toggleUnitSystem = () => {
    updateSetting('unitSystem', settings.unitSystem === 'imperial' ? 'metric' : 'imperial');
  };

  return (
    <GlassCard>
      <div className="p-4">
        <h2 className="text-lg font-semibold text-slate-800 mb-4">Appearance & Units</h2>

        <div className="space-y-4">
          {/* Dark Mode Toggle */}
          <div className="flex items-center justify-between">
            <div>
              <p className="font-medium text-slate-800">Dark Mode</p>
              <p className="text-sm text-slate-500">Switch to dark theme</p>
            </div>
            <button
              onClick={toggleDarkMode}
              className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
                settings.darkMode ? 'bg-blue-600' : 'bg-gray-200'
              }`}
            >
              <span
                className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
                  settings.darkMode ? 'translate-x-6' : 'translate-x-1'
                }`}
              />
            </button>
          </div>

          {/* Unit System Toggle */}
          <div className="flex items-center justify-between">
            <div>
              <p className="font-medium text-slate-800">Unit System</p>
              <p className="text-sm text-slate-500">
                Currently using {settings.unitSystem === 'imperial' ? 'Miles & Gallons' : 'Kilometers & Liters'}
              </p>
            </div>
            <button
              onClick={toggleUnitSystem}
              className="px-4 py-2 bg-blue-100 text-blue-700 rounded-lg text-sm font-medium"
            >
              {settings.unitSystem === 'imperial' ? 'Switch to Metric' : 'Switch to Imperial'}
            </button>
          </div>
        </div>
      </div>
    </GlassCard>
  );
};

Account Actions Section Component

File: frontend/src/features/settings/mobile/AccountActionsSection.tsx

import React, { useState } from 'react';
import { useAuth0 } from '@auth0/auth0-react';
import { GlassCard } from '../../../shared-minimal/components/mobile';

export const AccountActionsSection: React.FC = () => {
  const { logout } = useAuth0();
  const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);

  const handleLogout = () => {
    logout({
      logoutParams: {
        returnTo: window.location.origin
      }
    });
  };

  const handleDeleteAccount = () => {
    // Implementation for account deletion
    setShowDeleteConfirm(false);
    // Navigate to account deletion flow
  };

  return (
    <GlassCard>
      <div className="p-4">
        <h2 className="text-lg font-semibold text-slate-800 mb-4">Account Actions</h2>

        <div className="space-y-3">
          <button
            onClick={handleLogout}
            className="w-full py-3 px-4 bg-gray-100 text-gray-700 rounded-lg text-left font-medium hover:bg-gray-200 transition-colors"
          >
            Sign Out
          </button>

          <button
            onClick={() => setShowDeleteConfirm(true)}
            className="w-full py-3 px-4 bg-red-50 text-red-600 rounded-lg text-left font-medium hover:bg-red-100 transition-colors"
          >
            Delete Account
          </button>
        </div>

        {/* Delete Confirmation Modal */}
        {showDeleteConfirm && (
          <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
            <div className="bg-white rounded-lg p-6 max-w-sm w-full">
              <h3 className="text-lg font-semibold text-slate-800 mb-2">Delete Account</h3>
              <p className="text-slate-600 mb-4">
                This action cannot be undone. All your data will be permanently deleted.
              </p>
              <div className="flex space-x-3">
                <button
                  onClick={() => setShowDeleteConfirm(false)}
                  className="flex-1 py-2 px-4 bg-gray-200 text-gray-700 rounded-lg font-medium"
                >
                  Cancel
                </button>
                <button
                  onClick={handleDeleteAccount}
                  className="flex-1 py-2 px-4 bg-red-600 text-white rounded-lg font-medium"
                >
                  Delete
                </button>
              </div>
            </div>
          </div>
        )}
      </div>
    </GlassCard>
  );
};

Step 4: Implement Settings State Management

Settings Hook

File: frontend/src/features/settings/hooks/useSettings.ts

import { useState, useEffect } from 'react';
import { useSettingsPersistence } from './useSettingsPersistence';

export interface SettingsState {
  darkMode: boolean;
  unitSystem: 'imperial' | 'metric';
  notifications: {
    email: boolean;
    push: boolean;
    maintenance: boolean;
  };
}

const defaultSettings: SettingsState = {
  darkMode: false,
  unitSystem: 'imperial',
  notifications: {
    email: true,
    push: true,
    maintenance: true,
  },
};

export const useSettings = () => {
  const { loadSettings, saveSettings } = useSettingsPersistence();
  const [settings, setSettings] = useState<SettingsState>(defaultSettings);

  useEffect(() => {
    const savedSettings = loadSettings();
    if (savedSettings) {
      setSettings(savedSettings);
    }
  }, [loadSettings]);

  const updateSetting = <K extends keyof SettingsState>(
    key: K,
    value: SettingsState[K]
  ) => {
    const newSettings = { ...settings, [key]: value };
    setSettings(newSettings);
    saveSettings(newSettings);
  };

  return {
    settings,
    updateSetting,
  };
};

Settings Persistence Hook

File: frontend/src/features/settings/hooks/useSettingsPersistence.ts

import { useCallback } from 'react';
import { SettingsState } from './useSettings';

const SETTINGS_STORAGE_KEY = 'motovaultpro-mobile-settings';

export const useSettingsPersistence = () => {
  const loadSettings = useCallback((): SettingsState | null => {
    try {
      const stored = localStorage.getItem(SETTINGS_STORAGE_KEY);
      return stored ? JSON.parse(stored) : null;
    } catch (error) {
      console.error('Error loading settings:', error);
      return null;
    }
  }, []);

  const saveSettings = useCallback((settings: SettingsState) => {
    try {
      localStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(settings));
    } catch (error) {
      console.error('Error saving settings:', error);
    }
  }, []);

  return {
    loadSettings,
    saveSettings,
  };
};

Step 5: Update App.tsx Integration

File: frontend/src/App.tsx

Replace the existing placeholder SettingsScreen with:

// Import the new component
import { MobileSettingsScreen } from './features/settings/mobile/MobileSettingsScreen';

// Replace the existing SettingsScreen component (around line 113)
const SettingsScreen = MobileSettingsScreen;

Step 6: Integration with Existing Systems

Unit System Integration

Ensure mobile settings integrate with existing unit system:

File: frontend/src/shared-minimal/utils/units.ts

The mobile settings should use the existing unit conversion utilities and persist to the same storage key (motovaultpro-unit-system).

Zustand Store Integration

File: frontend/src/core/store/index.ts

Extend the existing store to include settings state if needed for cross-component access.

Testing Requirements

Mobile Testing Checklist

  • Settings screen renders correctly on mobile devices
  • All sections (Account, Notifications, Appearance, Data, Actions) function properly
  • Dark mode toggle works and persists
  • Unit system changes work and persist
  • Logout functionality works correctly
  • Account deletion flow works (with confirmation)
  • Settings persist across app restarts
  • Navigation to/from settings maintains context

Desktop Compatibility Testing

  • Changes don't break existing desktop settings
  • Settings synchronize between mobile and desktop views
  • Unit system changes reflect in both interfaces
  • Authentication flows remain consistent

Integration Testing

  • Settings integrate properly with existing Auth0 authentication
  • Unit preferences work across all features (vehicles, fuel logs, etc.)
  • Settings state management doesn't conflict with existing Zustand store
  • localStorage persistence works correctly

Migration Strategy

Phase 1: Component Creation

  1. Create the mobile settings directory structure
  2. Implement individual settings section components
  3. Create settings hooks for state management

Phase 2: Integration

  1. Replace placeholder in App.tsx
  2. Test mobile settings functionality
  3. Verify persistence and state management

Phase 3: Enhancement

  1. Add any missing features from desktop version
  2. Implement mobile-specific optimizations
  3. Ensure full feature parity

Success Criteria

Upon completion, the mobile settings should:

  1. Feature Parity: Match all desktop settings functionality
  2. Mobile-Optimized: Use appropriate mobile UI patterns and components
  3. Persistent: All settings persist across app restarts
  4. Integrated: Work seamlessly with existing authentication and state management
  5. Tested: Pass all mobile and desktop compatibility tests

This implementation will eliminate the critical mobile settings gap and provide a comprehensive settings experience across all platforms.