mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 04:11:14 -05:00
Refactor profile stats calculation
This commit is contained in:
@@ -48,6 +48,11 @@ export default function Profile() {
|
|||||||
const [formErrors, setFormErrors] = useState<Record<string, string>>({});
|
const [formErrors, setFormErrors] = useState<Record<string, string>>({});
|
||||||
const [avatarUrl, setAvatarUrl] = useState<string>('');
|
const [avatarUrl, setAvatarUrl] = useState<string>('');
|
||||||
const [avatarImageId, setAvatarImageId] = useState<string>('');
|
const [avatarImageId, setAvatarImageId] = useState<string>('');
|
||||||
|
const [calculatedStats, setCalculatedStats] = useState({
|
||||||
|
rideCount: 0,
|
||||||
|
coasterCount: 0,
|
||||||
|
parkCount: 0
|
||||||
|
});
|
||||||
|
|
||||||
// Username validation
|
// Username validation
|
||||||
const usernameValidation = useUsernameValidation(editForm.username, profile?.username);
|
const usernameValidation = useUsernameValidation(editForm.username, profile?.username);
|
||||||
@@ -59,6 +64,50 @@ export default function Profile() {
|
|||||||
fetchCurrentUserProfile();
|
fetchCurrentUserProfile();
|
||||||
}
|
}
|
||||||
}, [username]);
|
}, [username]);
|
||||||
|
|
||||||
|
const fetchCalculatedStats = async (userId: string) => {
|
||||||
|
try {
|
||||||
|
// Fetch ride credits stats
|
||||||
|
const { data: ridesData, error: ridesError } = await supabase
|
||||||
|
.from('user_ride_credits')
|
||||||
|
.select(`
|
||||||
|
ride_count,
|
||||||
|
rides!inner(category, park_id)
|
||||||
|
`)
|
||||||
|
.eq('user_id', userId);
|
||||||
|
|
||||||
|
if (ridesError) throw ridesError;
|
||||||
|
|
||||||
|
// Calculate total rides count (sum of all ride_count values)
|
||||||
|
const totalRides = ridesData?.reduce((sum, credit) => sum + (credit.ride_count || 0), 0) || 0;
|
||||||
|
|
||||||
|
// Calculate coasters count (distinct rides where category is roller_coaster)
|
||||||
|
const coasterRides = ridesData?.filter(credit =>
|
||||||
|
credit.rides?.category === 'roller_coaster'
|
||||||
|
) || [];
|
||||||
|
const uniqueCoasters = new Set(coasterRides.map(credit => credit.rides));
|
||||||
|
const coasterCount = uniqueCoasters.size;
|
||||||
|
|
||||||
|
// Calculate parks count (distinct parks where user has ridden at least one ride)
|
||||||
|
const parkRides = ridesData?.map(credit => credit.rides?.park_id).filter(Boolean) || [];
|
||||||
|
const uniqueParks = new Set(parkRides);
|
||||||
|
const parkCount = uniqueParks.size;
|
||||||
|
|
||||||
|
setCalculatedStats({
|
||||||
|
rideCount: totalRides,
|
||||||
|
coasterCount: coasterCount,
|
||||||
|
parkCount: parkCount
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('Error fetching calculated stats:', error);
|
||||||
|
// Set defaults on error
|
||||||
|
setCalculatedStats({
|
||||||
|
rideCount: 0,
|
||||||
|
coasterCount: 0,
|
||||||
|
parkCount: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
const getCurrentUser = async () => {
|
const getCurrentUser = async () => {
|
||||||
const {
|
const {
|
||||||
data: {
|
data: {
|
||||||
@@ -69,11 +118,14 @@ export default function Profile() {
|
|||||||
};
|
};
|
||||||
const fetchProfile = async (profileUsername: string) => {
|
const fetchProfile = async (profileUsername: string) => {
|
||||||
try {
|
try {
|
||||||
const {
|
const { data, error } = await supabase
|
||||||
data,
|
.from('profiles')
|
||||||
error
|
.select(`*, location:locations(*)`)
|
||||||
} = await supabase.from('profiles').select(`*, location:locations(*)`).eq('username', profileUsername).maybeSingle();
|
.eq('username', profileUsername)
|
||||||
|
.maybeSingle();
|
||||||
|
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
setProfile(data as ProfileType);
|
setProfile(data as ProfileType);
|
||||||
setEditForm({
|
setEditForm({
|
||||||
@@ -83,6 +135,9 @@ export default function Profile() {
|
|||||||
});
|
});
|
||||||
setAvatarUrl(data.avatar_url || '');
|
setAvatarUrl(data.avatar_url || '');
|
||||||
setAvatarImageId(data.avatar_image_id || '');
|
setAvatarImageId(data.avatar_image_id || '');
|
||||||
|
|
||||||
|
// Fetch calculated stats for this user
|
||||||
|
await fetchCalculatedStats(data.user_id);
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Error fetching profile:', error);
|
console.error('Error fetching profile:', error);
|
||||||
@@ -97,20 +152,20 @@ export default function Profile() {
|
|||||||
};
|
};
|
||||||
const fetchCurrentUserProfile = async () => {
|
const fetchCurrentUserProfile = async () => {
|
||||||
try {
|
try {
|
||||||
const {
|
const { data: { user } } = await supabase.auth.getUser();
|
||||||
data: {
|
|
||||||
user
|
|
||||||
}
|
|
||||||
} = await supabase.auth.getUser();
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
navigate('/auth');
|
navigate('/auth');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const {
|
|
||||||
data,
|
const { data, error } = await supabase
|
||||||
error
|
.from('profiles')
|
||||||
} = await supabase.from('profiles').select(`*, location:locations(*)`).eq('user_id', user.id).maybeSingle();
|
.select(`*, location:locations(*)`)
|
||||||
|
.eq('user_id', user.id)
|
||||||
|
.maybeSingle();
|
||||||
|
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
setProfile(data as ProfileType);
|
setProfile(data as ProfileType);
|
||||||
setEditForm({
|
setEditForm({
|
||||||
@@ -120,6 +175,9 @@ export default function Profile() {
|
|||||||
});
|
});
|
||||||
setAvatarUrl(data.avatar_url || '');
|
setAvatarUrl(data.avatar_url || '');
|
||||||
setAvatarImageId(data.avatar_image_id || '');
|
setAvatarImageId(data.avatar_image_id || '');
|
||||||
|
|
||||||
|
// Fetch calculated stats for the current user
|
||||||
|
await fetchCalculatedStats(user.id);
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Error fetching profile:', error);
|
console.error('Error fetching profile:', error);
|
||||||
@@ -410,19 +468,19 @@ export default function Profile() {
|
|||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="p-4 text-center">
|
<CardContent className="p-4 text-center">
|
||||||
<div className="text-2xl font-bold text-primary">{profile.ride_count}</div>
|
<div className="text-2xl font-bold text-primary">{calculatedStats.rideCount}</div>
|
||||||
<div className="text-sm text-muted-foreground">Rides</div>
|
<div className="text-sm text-muted-foreground">Rides</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="p-4 text-center">
|
<CardContent className="p-4 text-center">
|
||||||
<div className="text-2xl font-bold text-accent">{profile.coaster_count}</div>
|
<div className="text-2xl font-bold text-accent">{calculatedStats.coasterCount}</div>
|
||||||
<div className="text-sm text-muted-foreground">Coasters</div>
|
<div className="text-sm text-muted-foreground">Coasters</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="p-4 text-center">
|
<CardContent className="p-4 text-center">
|
||||||
<div className="text-2xl font-bold text-secondary">{profile.park_count}</div>
|
<div className="text-2xl font-bold text-secondary">{calculatedStats.parkCount}</div>
|
||||||
<div className="text-sm text-muted-foreground">Parks</div>
|
<div className="text-sm text-muted-foreground">Parks</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Reference in New Issue
Block a user