error and load state
This commit is contained in:
@@ -14,6 +14,11 @@ import type {
|
||||
DriverProfileViewModel
|
||||
} from '@/lib/view-models/DriverProfileViewModel';
|
||||
import { getMediaUrl } from '@/lib/utilities/media';
|
||||
|
||||
// Shared state components
|
||||
import { useDataFetching } from '@/components/shared/hooks/useDataFetching';
|
||||
import { StateContainer } from '@/components/shared/state/StateContainer';
|
||||
import { LoadingWrapper } from '@/components/shared/state/LoadingWrapper';
|
||||
import {
|
||||
Activity,
|
||||
Award,
|
||||
@@ -260,34 +265,19 @@ export default function ProfilePage() {
|
||||
|
||||
const { driverService, mediaService } = useServices();
|
||||
|
||||
const [profileData, setProfileData] = useState<DriverProfileViewModel | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [editMode, setEditMode] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState<ProfileTab>(tabParam || 'overview');
|
||||
const [friendRequestSent, setFriendRequestSent] = useState(false);
|
||||
|
||||
const effectiveDriverId = useEffectiveDriverId();
|
||||
const isOwnProfile = true; // This page is always your own profile
|
||||
|
||||
useEffect(() => {
|
||||
if (!effectiveDriverId) {
|
||||
return;
|
||||
}
|
||||
// Shared state components
|
||||
const { data: profileData, isLoading: loading, error, retry } = useDataFetching({
|
||||
queryKey: ['driverProfile', effectiveDriverId],
|
||||
queryFn: () => driverService.getDriverProfile(effectiveDriverId),
|
||||
enabled: !!effectiveDriverId,
|
||||
});
|
||||
|
||||
const loadData = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const profileViewModel = await driverService.getDriverProfile(effectiveDriverId);
|
||||
setProfileData(profileViewModel);
|
||||
} catch (error) {
|
||||
console.error('Failed to load profile:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
void loadData();
|
||||
}, [effectiveDriverId, driverService]);
|
||||
const [editMode, setEditMode] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState<ProfileTab>(tabParam || 'overview');
|
||||
const [friendRequestSent, setFriendRequestSent] = useState(false);
|
||||
|
||||
// Update URL when tab changes
|
||||
useEffect(() => {
|
||||
@@ -315,7 +305,8 @@ export default function ProfilePage() {
|
||||
|
||||
try {
|
||||
const updatedProfile = await driverService.updateProfile(updates);
|
||||
setProfileData(updatedProfile);
|
||||
// Update local state
|
||||
retry();
|
||||
setEditMode(false);
|
||||
} catch (error) {
|
||||
console.error('Failed to update profile:', error);
|
||||
@@ -327,20 +318,8 @@ export default function ProfilePage() {
|
||||
// In production, this would call a use case
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="max-w-6xl mx-auto px-4">
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="w-10 h-10 border-2 border-primary-blue border-t-transparent rounded-full animate-spin" />
|
||||
<p className="text-gray-400">Loading profile...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!profileData?.currentDriver) {
|
||||
// Show create form if no profile exists
|
||||
if (!loading && !profileData?.currentDriver && !error) {
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-4">
|
||||
<div className="text-center mb-8">
|
||||
@@ -366,16 +345,8 @@ export default function ProfilePage() {
|
||||
);
|
||||
}
|
||||
|
||||
// Extract data from profileData ViewModel
|
||||
const currentDriver = profileData.currentDriver;
|
||||
const stats = profileData.stats;
|
||||
const teamMemberships = profileData.teamMemberships;
|
||||
const socialSummary = profileData.socialSummary;
|
||||
const extendedProfile = profileData.extendedProfile;
|
||||
const globalRank = currentDriver?.globalRank || null;
|
||||
|
||||
// Show edit mode
|
||||
if (editMode) {
|
||||
if (editMode && profileData?.currentDriver) {
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-4 space-y-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
@@ -390,7 +361,49 @@ export default function ProfilePage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-7xl mx-auto px-4 pb-12 space-y-6">
|
||||
<StateContainer
|
||||
data={profileData}
|
||||
isLoading={loading}
|
||||
error={error}
|
||||
retry={retry}
|
||||
config={{
|
||||
loading: { variant: 'full-screen', message: 'Loading profile...' },
|
||||
error: { variant: 'full-screen' },
|
||||
empty: {
|
||||
icon: User,
|
||||
title: 'No profile data',
|
||||
description: 'Unable to load your profile information',
|
||||
action: { label: 'Retry', onClick: retry }
|
||||
}
|
||||
}}
|
||||
>
|
||||
{(profileData) => {
|
||||
// Extract data from profileData ViewModel
|
||||
// At this point, we know profileData exists and currentDriver should exist
|
||||
// (otherwise we would have shown the create form above)
|
||||
const currentDriver = profileData.currentDriver;
|
||||
|
||||
// If currentDriver is null despite our checks, show empty state
|
||||
if (!currentDriver) {
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-4">
|
||||
<Card className="text-center py-12">
|
||||
<User className="w-16 h-16 text-gray-600 mx-auto mb-4" />
|
||||
<p className="text-gray-400 mb-2">No driver profile found</p>
|
||||
<p className="text-sm text-gray-500">Please create a driver profile to continue</p>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const stats = profileData.stats;
|
||||
const teamMemberships = profileData.teamMemberships;
|
||||
const socialSummary = profileData.socialSummary;
|
||||
const extendedProfile = profileData.extendedProfile;
|
||||
const globalRank = currentDriver.globalRank || null;
|
||||
|
||||
return (
|
||||
<div className="max-w-7xl mx-auto px-4 pb-12 space-y-6">
|
||||
{/* Hero Header Section */}
|
||||
<div className="relative rounded-2xl overflow-hidden bg-gradient-to-br from-iron-gray/80 via-iron-gray/60 to-deep-graphite border border-charcoal-outline">
|
||||
{/* Background Pattern */}
|
||||
@@ -1045,13 +1058,16 @@ export default function ProfilePage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'stats' && !stats && (
|
||||
<Card className="text-center py-12">
|
||||
<BarChart3 className="w-16 h-16 text-gray-600 mx-auto mb-4" />
|
||||
<p className="text-gray-400 mb-2">No statistics available yet</p>
|
||||
<p className="text-sm text-gray-500">Join a league and complete races to see detailed stats</p>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
{activeTab === 'stats' && !stats && (
|
||||
<Card className="text-center py-12">
|
||||
<BarChart3 className="w-16 h-16 text-gray-600 mx-auto mb-4" />
|
||||
<p className="text-gray-400 mb-2">No statistics available yet</p>
|
||||
<p className="text-sm text-gray-500">Join a league and complete races to see detailed stats</p>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</StateContainer>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user