feat: Add admin vehicle management and profile vehicles display (refs #11)
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 4m34s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 37s
Deploy to Staging / Verify Staging (pull_request) Successful in 6s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 6s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped

- Add GET /api/admin/stats endpoint for Total Vehicles widget
- Add GET /api/admin/users/:auth0Sub/vehicles endpoint for user vehicle list
- Update AdminUsersPage with Total Vehicles stat and expandable vehicle rows
- Add My Vehicles section to SettingsPage (desktop) and MobileSettingsScreen
- Update AdminUsersMobileScreen with stats header and vehicle expansion
- Add defense-in-depth admin checks and error handling
- Update admin README documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Eric Gullickson
2026-01-04 13:18:38 -06:00
parent 2ec208e25a
commit 4fc5b391e1
10 changed files with 639 additions and 67 deletions

View File

@@ -5,6 +5,7 @@ import { MobileContainer } from '../../../shared-minimal/components/mobile/Mobil
import { useSettings } from '../hooks/useSettings';
import { useProfile, useUpdateProfile } from '../hooks/useProfile';
import { useExportUserData } from '../hooks/useExportUserData';
import { useVehicles } from '../../vehicles/hooks/useVehicles';
import { useAdminAccess } from '../../../core/auth/useAdminAccess';
import { useNavigationStore } from '../../../core/store';
import { DeleteAccountModal } from './DeleteAccountModal';
@@ -80,6 +81,7 @@ export const MobileSettingsScreen: React.FC = () => {
const { data: profile, isLoading: profileLoading } = useProfile();
const updateProfileMutation = useUpdateProfile();
const exportMutation = useExportUserData();
const { data: vehicles, isLoading: vehiclesLoading } = useVehicles();
const { isAdmin, loading: adminLoading } = useAdminAccess();
const [showDataExport, setShowDataExport] = useState(false);
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
@@ -316,6 +318,58 @@ export const MobileSettingsScreen: React.FC = () => {
</div>
</GlassCard>
{/* My Vehicles Section */}
<GlassCard padding="md">
<div>
<div className="flex justify-between items-center mb-4">
<div className="flex items-center gap-2">
<svg className="w-5 h-5 text-primary-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 4H5c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h4l3 3 3-3h4c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM8 11V9h8v2m-8 4v-2h5v2" />
</svg>
<h2 className="text-lg font-semibold text-slate-800 dark:text-avus">
My Vehicles
</h2>
{!vehiclesLoading && vehicles && (
<span className="text-sm text-slate-500 dark:text-titanio">({vehicles.length})</span>
)}
</div>
<button
onClick={() => navigateToScreen('Vehicles')}
className="px-3 py-1.5 bg-primary-500 text-white rounded-lg text-sm font-medium hover:bg-primary-600 transition-colors dark:bg-primary-600 dark:hover:bg-primary-700"
style={{ minHeight: '44px', minWidth: '44px' }}
>
Manage
</button>
</div>
{vehiclesLoading ? (
<div className="flex justify-center py-4">
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-primary-500"></div>
</div>
) : !vehicles?.length ? (
<p className="text-sm text-slate-500 dark:text-titanio py-2">
No vehicles registered. Add your first vehicle to get started.
</p>
) : (
<div className="space-y-2">
{vehicles.map((vehicle) => (
<div
key={vehicle.id}
className="p-3 bg-slate-50 dark:bg-nero rounded-lg"
>
<p className="font-medium text-slate-800 dark:text-avus">
{vehicle.year} {vehicle.make} {vehicle.model}
</p>
{vehicle.nickname && (
<p className="text-sm text-slate-500 dark:text-titanio">{vehicle.nickname}</p>
)}
</div>
))}
</div>
)}
</div>
</GlassCard>
{/* Notifications Section */}
<GlassCard padding="md">
<div>