Initial Commit
This commit is contained in:
323
frontend/src/features/settings/mobile/MobileSettingsScreen.tsx
Normal file
323
frontend/src/features/settings/mobile/MobileSettingsScreen.tsx
Normal file
@@ -0,0 +1,323 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useAuth0 } from '@auth0/auth0-react';
|
||||
import { GlassCard } from '../../../shared-minimal/components/mobile/GlassCard';
|
||||
import { MobileContainer } from '../../../shared-minimal/components/mobile/MobileContainer';
|
||||
import { useSettings } from '../hooks/useSettings';
|
||||
|
||||
interface ToggleSwitchProps {
|
||||
enabled: boolean;
|
||||
onChange: () => void;
|
||||
label: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
const ToggleSwitch: React.FC<ToggleSwitchProps> = ({
|
||||
enabled,
|
||||
onChange,
|
||||
label,
|
||||
description
|
||||
}) => (
|
||||
<div className="flex items-center justify-between py-2">
|
||||
<div>
|
||||
<p className="font-medium text-slate-800">{label}</p>
|
||||
{description && (
|
||||
<p className="text-sm text-slate-500">{description}</p>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
onClick={onChange}
|
||||
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
|
||||
enabled ? 'bg-blue-600' : 'bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
|
||||
enabled ? 'translate-x-6' : 'translate-x-1'
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
interface ModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const Modal: React.FC<ModalProps> = ({ isOpen, onClose, title, children }) => {
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<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-4">{title}</h3>
|
||||
{children}
|
||||
<div className="flex justify-end mt-4">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg font-medium"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const MobileSettingsScreen: React.FC = () => {
|
||||
const { user, logout } = useAuth0();
|
||||
const { settings, updateSetting, isLoading, error } = useSettings();
|
||||
const [showDataExport, setShowDataExport] = useState(false);
|
||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||
|
||||
const handleLogout = () => {
|
||||
logout({
|
||||
logoutParams: {
|
||||
returnTo: window.location.origin
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleExportData = () => {
|
||||
// TODO: Implement data export functionality
|
||||
console.log('Exporting user data...');
|
||||
setShowDataExport(false);
|
||||
};
|
||||
|
||||
const handleDeleteAccount = () => {
|
||||
// TODO: Implement account deletion
|
||||
console.log('Deleting account...');
|
||||
setShowDeleteConfirm(false);
|
||||
};
|
||||
|
||||
// Loading state
|
||||
if (isLoading) {
|
||||
return (
|
||||
<MobileContainer>
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-center">
|
||||
<div className="text-slate-500 mb-2">Loading settings...</div>
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto"></div>
|
||||
</div>
|
||||
</div>
|
||||
</MobileContainer>
|
||||
);
|
||||
}
|
||||
|
||||
// Error state
|
||||
if (error) {
|
||||
return (
|
||||
<MobileContainer>
|
||||
<div className="space-y-4 pb-20 p-4">
|
||||
<GlassCard padding="md">
|
||||
<div className="text-center py-8">
|
||||
<p className="text-red-600 mb-4">Failed to load settings</p>
|
||||
<p className="text-sm text-slate-600 mb-4">{error}</p>
|
||||
<button
|
||||
onClick={() => window.location.reload()}
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded-lg"
|
||||
>
|
||||
Retry
|
||||
</button>
|
||||
</div>
|
||||
</GlassCard>
|
||||
</div>
|
||||
</MobileContainer>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<MobileContainer>
|
||||
<div className="space-y-4 pb-20 p-4">
|
||||
{/* Header */}
|
||||
<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>
|
||||
|
||||
{/* Account Section */}
|
||||
<GlassCard padding="md">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-slate-800 mb-4">Account</h2>
|
||||
<div className="flex items-center space-x-3">
|
||||
{user?.picture && (
|
||||
<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-3 mt-3 border-t border-slate-200">
|
||||
<p className="text-sm text-slate-600">
|
||||
Member since {user?.updated_at ? new Date(user.updated_at).toLocaleDateString() : 'Unknown'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</GlassCard>
|
||||
|
||||
{/* Notifications Section */}
|
||||
<GlassCard padding="md">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-slate-800 mb-4">Notifications</h2>
|
||||
<div className="space-y-3">
|
||||
<ToggleSwitch
|
||||
enabled={settings.notifications.email}
|
||||
onChange={() => updateSetting('notifications', {
|
||||
...settings.notifications,
|
||||
email: !settings.notifications.email
|
||||
})}
|
||||
label="Email Notifications"
|
||||
description="Receive updates via email"
|
||||
/>
|
||||
<ToggleSwitch
|
||||
enabled={settings.notifications.push}
|
||||
onChange={() => updateSetting('notifications', {
|
||||
...settings.notifications,
|
||||
push: !settings.notifications.push
|
||||
})}
|
||||
label="Push Notifications"
|
||||
description="Receive mobile push notifications"
|
||||
/>
|
||||
<ToggleSwitch
|
||||
enabled={settings.notifications.maintenance}
|
||||
onChange={() => updateSetting('notifications', {
|
||||
...settings.notifications,
|
||||
maintenance: !settings.notifications.maintenance
|
||||
})}
|
||||
label="Maintenance Reminders"
|
||||
description="Get reminded about vehicle maintenance"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</GlassCard>
|
||||
|
||||
{/* Appearance & Units Section */}
|
||||
<GlassCard padding="md">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-slate-800 mb-4">Appearance & Units</h2>
|
||||
<div className="space-y-4">
|
||||
<ToggleSwitch
|
||||
enabled={settings.darkMode}
|
||||
onChange={() => updateSetting('darkMode', !settings.darkMode)}
|
||||
label="Dark Mode"
|
||||
description="Switch to dark theme"
|
||||
/>
|
||||
|
||||
<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={() => updateSetting('unitSystem', settings.unitSystem === 'imperial' ? 'metric' : 'imperial')}
|
||||
className="px-4 py-2 bg-blue-100 text-blue-700 rounded-lg text-sm font-medium hover:bg-blue-200 transition-colors"
|
||||
>
|
||||
{settings.unitSystem === 'imperial' ? 'Switch to Metric' : 'Switch to Imperial'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</GlassCard>
|
||||
|
||||
{/* Data Management Section */}
|
||||
<GlassCard padding="md">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-slate-800 mb-4">Data Management</h2>
|
||||
<div className="space-y-3">
|
||||
<button
|
||||
onClick={() => setShowDataExport(true)}
|
||||
className="w-full text-left p-3 bg-blue-50 text-blue-700 rounded-lg font-medium hover:bg-blue-100 transition-colors"
|
||||
>
|
||||
Export My Data
|
||||
</button>
|
||||
<p className="text-sm text-slate-500">
|
||||
Download a copy of all your vehicle and fuel data
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</GlassCard>
|
||||
|
||||
{/* Account Actions Section */}
|
||||
<GlassCard padding="md">
|
||||
<div>
|
||||
<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>
|
||||
</div>
|
||||
</GlassCard>
|
||||
|
||||
{/* Data Export Modal */}
|
||||
<Modal
|
||||
isOpen={showDataExport}
|
||||
onClose={() => setShowDataExport(false)}
|
||||
title="Export Data"
|
||||
>
|
||||
<p className="text-slate-600 mb-4">
|
||||
This will create a downloadable file containing all your vehicle data, fuel logs, and preferences.
|
||||
</p>
|
||||
<div className="flex space-x-3">
|
||||
<button
|
||||
onClick={() => setShowDataExport(false)}
|
||||
className="flex-1 py-2 px-4 bg-gray-200 text-gray-700 rounded-lg font-medium"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={handleExportData}
|
||||
className="flex-1 py-2 px-4 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
Export
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
{/* Delete Account Confirmation */}
|
||||
<Modal
|
||||
isOpen={showDeleteConfirm}
|
||||
onClose={() => setShowDeleteConfirm(false)}
|
||||
title="Delete Account"
|
||||
>
|
||||
<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 hover:bg-red-700 transition-colors"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
</MobileContainer>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user