mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 11:51:14 -05:00
Visual edit in Lovable
This commit is contained in:
@@ -13,23 +13,7 @@ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from '@/components/ui/alert-dialog';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
import { useUsernameValidation } from '@/hooks/useUsernameValidation';
|
||||
import {
|
||||
User,
|
||||
MapPin,
|
||||
Calendar,
|
||||
Star,
|
||||
Trophy,
|
||||
Settings,
|
||||
Camera,
|
||||
Edit3,
|
||||
Save,
|
||||
X,
|
||||
ArrowLeft,
|
||||
Check,
|
||||
AlertCircle,
|
||||
Loader2,
|
||||
UserX
|
||||
} from 'lucide-react';
|
||||
import { User, MapPin, Calendar, Star, Trophy, Settings, Camera, Edit3, Save, X, ArrowLeft, Check, AlertCircle, Loader2, UserX } from 'lucide-react';
|
||||
import { Profile as ProfileType } from '@/types/database';
|
||||
import { supabase } from '@/integrations/supabase/client';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
@@ -37,12 +21,19 @@ import { PhotoUpload } from '@/components/upload/PhotoUpload';
|
||||
import { profileEditSchema } from '@/lib/validation';
|
||||
import { LocationDisplay } from '@/components/profile/LocationDisplay';
|
||||
import { UserBlockButton } from '@/components/profile/UserBlockButton';
|
||||
|
||||
export default function Profile() {
|
||||
const { username } = useParams<{ username?: string }>();
|
||||
const {
|
||||
username
|
||||
} = useParams<{
|
||||
username?: string;
|
||||
}>();
|
||||
const navigate = useNavigate();
|
||||
const { toast } = useToast();
|
||||
const { refreshProfile } = useAuth();
|
||||
const {
|
||||
toast
|
||||
} = useToast();
|
||||
const {
|
||||
refreshProfile
|
||||
} = useAuth();
|
||||
const [profile, setProfile] = useState<ProfileType | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [editing, setEditing] = useState(false);
|
||||
@@ -50,16 +41,15 @@ export default function Profile() {
|
||||
const [editForm, setEditForm] = useState({
|
||||
username: '',
|
||||
display_name: '',
|
||||
bio: '',
|
||||
bio: ''
|
||||
});
|
||||
const [showUsernameDialog, setShowUsernameDialog] = useState(false);
|
||||
const [formErrors, setFormErrors] = useState<Record<string, string>>({});
|
||||
const [avatarUrl, setAvatarUrl] = useState<string>('');
|
||||
const [avatarImageId, setAvatarImageId] = useState<string>('');
|
||||
|
||||
|
||||
// Username validation
|
||||
const usernameValidation = useUsernameValidation(editForm.username, profile?.username);
|
||||
|
||||
useEffect(() => {
|
||||
getCurrentUser();
|
||||
if (username) {
|
||||
@@ -68,28 +58,27 @@ export default function Profile() {
|
||||
fetchCurrentUserProfile();
|
||||
}
|
||||
}, [username]);
|
||||
|
||||
const getCurrentUser = async () => {
|
||||
const { data: { user } } = await supabase.auth.getUser();
|
||||
const {
|
||||
data: {
|
||||
user
|
||||
}
|
||||
} = await supabase.auth.getUser();
|
||||
setCurrentUser(user);
|
||||
};
|
||||
|
||||
const fetchProfile = async (profileUsername: string) => {
|
||||
try {
|
||||
const { data, error } = await supabase
|
||||
.from('profiles')
|
||||
.select(`*, location:locations(*)`)
|
||||
.eq('username', profileUsername)
|
||||
.maybeSingle();
|
||||
|
||||
const {
|
||||
data,
|
||||
error
|
||||
} = await supabase.from('profiles').select(`*, location:locations(*)`).eq('username', profileUsername).maybeSingle();
|
||||
if (error) throw error;
|
||||
|
||||
if (data) {
|
||||
setProfile(data as ProfileType);
|
||||
setEditForm({
|
||||
username: data.username || '',
|
||||
display_name: data.display_name || '',
|
||||
bio: data.bio || '',
|
||||
bio: data.bio || ''
|
||||
});
|
||||
setAvatarUrl(data.avatar_url || '');
|
||||
setAvatarImageId(data.avatar_image_id || '');
|
||||
@@ -99,36 +88,34 @@ export default function Profile() {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error loading profile",
|
||||
description: error.message,
|
||||
description: error.message
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchCurrentUserProfile = async () => {
|
||||
try {
|
||||
const { data: { user } } = await supabase.auth.getUser();
|
||||
|
||||
const {
|
||||
data: {
|
||||
user
|
||||
}
|
||||
} = await supabase.auth.getUser();
|
||||
if (!user) {
|
||||
navigate('/auth');
|
||||
return;
|
||||
}
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('profiles')
|
||||
.select(`*, location:locations(*)`)
|
||||
.eq('user_id', user.id)
|
||||
.maybeSingle();
|
||||
|
||||
const {
|
||||
data,
|
||||
error
|
||||
} = await supabase.from('profiles').select(`*, location:locations(*)`).eq('user_id', user.id).maybeSingle();
|
||||
if (error) throw error;
|
||||
|
||||
if (data) {
|
||||
setProfile(data as ProfileType);
|
||||
setEditForm({
|
||||
username: data.username || '',
|
||||
display_name: data.display_name || '',
|
||||
bio: data.bio || '',
|
||||
bio: data.bio || ''
|
||||
});
|
||||
setAvatarUrl(data.avatar_url || '');
|
||||
setAvatarImageId(data.avatar_image_id || '');
|
||||
@@ -138,18 +125,17 @@ export default function Profile() {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error loading profile",
|
||||
description: error.message,
|
||||
description: error.message
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const validateForm = () => {
|
||||
const result = profileEditSchema.safeParse(editForm);
|
||||
if (!result.success) {
|
||||
const errors: Record<string, string> = {};
|
||||
result.error.issues.forEach((issue) => {
|
||||
result.error.issues.forEach(issue => {
|
||||
if (issue.path[0]) {
|
||||
errors[issue.path[0] as string] = issue.message;
|
||||
}
|
||||
@@ -157,28 +143,23 @@ export default function Profile() {
|
||||
setFormErrors(errors);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!usernameValidation.isValid && editForm.username !== profile?.username) {
|
||||
setFormErrors({ username: usernameValidation.error || 'Invalid username' });
|
||||
setFormErrors({
|
||||
username: usernameValidation.error || 'Invalid username'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
setFormErrors({});
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleSaveProfile = async () => {
|
||||
if (!profile || !currentUser) return;
|
||||
|
||||
if (!validateForm()) return;
|
||||
|
||||
const usernameChanged = editForm.username !== profile.username;
|
||||
|
||||
if (usernameChanged && !showUsernameDialog) {
|
||||
setShowUsernameDialog(true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const updateData: any = {
|
||||
display_name: editForm.display_name,
|
||||
@@ -186,73 +167,60 @@ export default function Profile() {
|
||||
avatar_url: avatarUrl,
|
||||
avatar_image_id: avatarImageId
|
||||
};
|
||||
|
||||
if (usernameChanged) {
|
||||
updateData.username = editForm.username;
|
||||
}
|
||||
|
||||
const { error } = await supabase
|
||||
.from('profiles')
|
||||
.update(updateData)
|
||||
.eq('user_id', currentUser.id);
|
||||
|
||||
const {
|
||||
error
|
||||
} = await supabase.from('profiles').update(updateData).eq('user_id', currentUser.id);
|
||||
if (error) throw error;
|
||||
|
||||
setProfile(prev => prev ? {
|
||||
...prev,
|
||||
...updateData
|
||||
} : null);
|
||||
|
||||
setEditing(false);
|
||||
setShowUsernameDialog(false);
|
||||
|
||||
if (usernameChanged) {
|
||||
toast({
|
||||
title: "Profile updated",
|
||||
description: "Your username and profile URL have been updated successfully.",
|
||||
description: "Your username and profile URL have been updated successfully."
|
||||
});
|
||||
// Navigate to new username URL
|
||||
navigate(`/profile/${editForm.username}`);
|
||||
} else {
|
||||
toast({
|
||||
title: "Profile updated",
|
||||
description: "Your profile has been updated successfully.",
|
||||
description: "Your profile has been updated successfully."
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error updating profile",
|
||||
description: error.message,
|
||||
description: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const confirmUsernameChange = () => {
|
||||
setShowUsernameDialog(false);
|
||||
handleSaveProfile();
|
||||
};
|
||||
|
||||
const handleAvatarUpload = async (urls: string[], imageId?: string) => {
|
||||
if (!currentUser || !urls[0]) return;
|
||||
|
||||
const newAvatarUrl = urls[0];
|
||||
const newImageId = imageId || '';
|
||||
|
||||
|
||||
// Update local state immediately
|
||||
setAvatarUrl(newAvatarUrl);
|
||||
setAvatarImageId(newImageId);
|
||||
|
||||
try {
|
||||
// Update database immediately
|
||||
const { error } = await supabase
|
||||
.from('profiles')
|
||||
.update({
|
||||
avatar_url: newAvatarUrl,
|
||||
avatar_image_id: newImageId
|
||||
})
|
||||
.eq('user_id', currentUser.id);
|
||||
|
||||
const {
|
||||
error
|
||||
} = await supabase.from('profiles').update({
|
||||
avatar_url: newAvatarUrl,
|
||||
avatar_image_id: newImageId
|
||||
}).eq('user_id', currentUser.id);
|
||||
if (error) throw error;
|
||||
|
||||
// Update local profile state
|
||||
@@ -266,29 +234,24 @@ export default function Profile() {
|
||||
if (refreshProfile) {
|
||||
await refreshProfile();
|
||||
}
|
||||
|
||||
toast({
|
||||
title: "Avatar updated",
|
||||
description: "Your profile picture has been updated successfully.",
|
||||
description: "Your profile picture has been updated successfully."
|
||||
});
|
||||
} catch (error: any) {
|
||||
// Revert local state on error
|
||||
setAvatarUrl(profile?.avatar_url || '');
|
||||
setAvatarImageId(profile?.avatar_image_id || '');
|
||||
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error updating avatar",
|
||||
description: error.message,
|
||||
description: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const isOwnProfile = currentUser && profile && currentUser.id === profile.user_id;
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
return <div className="min-h-screen bg-background">
|
||||
<Header />
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="animate-pulse space-y-6">
|
||||
@@ -297,13 +260,10 @@ export default function Profile() {
|
||||
<div className="h-4 bg-muted rounded w-1/2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
</div>;
|
||||
}
|
||||
|
||||
if (!profile) {
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
return <div className="min-h-screen bg-background">
|
||||
<Header />
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="text-center py-12">
|
||||
@@ -318,12 +278,9 @@ export default function Profile() {
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
return <div className="min-h-screen bg-background">
|
||||
<Header />
|
||||
|
||||
<main className="container mx-auto px-4 py-8">
|
||||
@@ -333,77 +290,40 @@ export default function Profile() {
|
||||
<CardContent className="p-8">
|
||||
<div className="flex flex-col md:flex-row gap-6">
|
||||
<div className="flex flex-col items-center md:items-start">
|
||||
<PhotoUpload
|
||||
variant="avatar"
|
||||
maxFiles={1}
|
||||
existingPhotos={profile.avatar_url ? [profile.avatar_url] : []}
|
||||
onUploadComplete={handleAvatarUpload}
|
||||
currentImageId={avatarImageId}
|
||||
onError={(error) => {
|
||||
toast({
|
||||
title: "Upload Error",
|
||||
description: error,
|
||||
variant: "destructive"
|
||||
});
|
||||
}}
|
||||
className="mb-4"
|
||||
/>
|
||||
<PhotoUpload variant="avatar" maxFiles={1} existingPhotos={profile.avatar_url ? [profile.avatar_url] : []} onUploadComplete={handleAvatarUpload} currentImageId={avatarImageId} onError={error => {
|
||||
toast({
|
||||
title: "Upload Error",
|
||||
description: error,
|
||||
variant: "destructive"
|
||||
});
|
||||
}} className="mb-4" />
|
||||
|
||||
<div className="flex flex-col gap-2 mt-2">
|
||||
{isOwnProfile && !editing && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setEditing(true)}
|
||||
>
|
||||
{isOwnProfile && !editing && <Button variant="outline" size="sm" onClick={() => setEditing(true)}>
|
||||
<Edit3 className="w-4 h-4 mr-2" />
|
||||
Edit Profile
|
||||
</Button>
|
||||
)}
|
||||
</Button>}
|
||||
|
||||
{!isOwnProfile && (
|
||||
<UserBlockButton
|
||||
targetUserId={profile.user_id}
|
||||
targetUsername={profile.username}
|
||||
/>
|
||||
)}
|
||||
{!isOwnProfile && <UserBlockButton targetUserId={profile.user_id} targetUsername={profile.username} />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1">
|
||||
{editing && isOwnProfile ? (
|
||||
<div className="space-y-4">
|
||||
{editing && isOwnProfile ? <div className="space-y-4">
|
||||
<div>
|
||||
<Label htmlFor="username">Username</Label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
id="username"
|
||||
value={editForm.username}
|
||||
onChange={(e) => setEditForm(prev => ({ ...prev, username: e.target.value }))}
|
||||
placeholder="your_username"
|
||||
className={`pr-10 ${formErrors.username ? 'border-destructive' : ''}`}
|
||||
/>
|
||||
<Input id="username" value={editForm.username} onChange={e => setEditForm(prev => ({
|
||||
...prev,
|
||||
username: e.target.value
|
||||
}))} placeholder="your_username" className={`pr-10 ${formErrors.username ? 'border-destructive' : ''}`} />
|
||||
<div className="absolute right-3 top-1/2 -translate-y-1/2">
|
||||
{usernameValidation.isChecking ? (
|
||||
<Loader2 className="w-4 h-4 text-muted-foreground animate-spin" />
|
||||
) : editForm.username === profile?.username ? (
|
||||
<Check className="w-4 h-4 text-muted-foreground" />
|
||||
) : usernameValidation.isValid ? (
|
||||
<Check className="w-4 h-4 text-green-500" />
|
||||
) : usernameValidation.error ? (
|
||||
<AlertCircle className="w-4 h-4 text-destructive" />
|
||||
) : null}
|
||||
{usernameValidation.isChecking ? <Loader2 className="w-4 h-4 text-muted-foreground animate-spin" /> : editForm.username === profile?.username ? <Check className="w-4 h-4 text-muted-foreground" /> : usernameValidation.isValid ? <Check className="w-4 h-4 text-green-500" /> : usernameValidation.error ? <AlertCircle className="w-4 h-4 text-destructive" /> : null}
|
||||
</div>
|
||||
</div>
|
||||
{formErrors.username && (
|
||||
<p className="text-sm text-destructive mt-1">{formErrors.username}</p>
|
||||
)}
|
||||
{usernameValidation.error && editForm.username !== profile?.username && (
|
||||
<p className="text-sm text-destructive mt-1">{usernameValidation.error}</p>
|
||||
)}
|
||||
{usernameValidation.isValid && editForm.username !== profile?.username && (
|
||||
<p className="text-sm text-green-600 mt-1">Username is available!</p>
|
||||
)}
|
||||
{formErrors.username && <p className="text-sm text-destructive mt-1">{formErrors.username}</p>}
|
||||
{usernameValidation.error && editForm.username !== profile?.username && <p className="text-sm text-destructive mt-1">{usernameValidation.error}</p>}
|
||||
{usernameValidation.isValid && editForm.username !== profile?.username && <p className="text-sm text-green-600 mt-1">Username is available!</p>}
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Your profile URL will be /profile/{editForm.username}
|
||||
</p>
|
||||
@@ -411,105 +331,71 @@ export default function Profile() {
|
||||
|
||||
<div>
|
||||
<Label htmlFor="display_name">Display Name</Label>
|
||||
<Input
|
||||
id="display_name"
|
||||
value={editForm.display_name}
|
||||
onChange={(e) => setEditForm(prev => ({ ...prev, display_name: e.target.value }))}
|
||||
placeholder="Your display name"
|
||||
className={formErrors.display_name ? 'border-destructive' : ''}
|
||||
/>
|
||||
{formErrors.display_name && (
|
||||
<p className="text-sm text-destructive mt-1">{formErrors.display_name}</p>
|
||||
)}
|
||||
<Input id="display_name" value={editForm.display_name} onChange={e => setEditForm(prev => ({
|
||||
...prev,
|
||||
display_name: e.target.value
|
||||
}))} placeholder="Your display name" className={formErrors.display_name ? 'border-destructive' : ''} />
|
||||
{formErrors.display_name && <p className="text-sm text-destructive mt-1">{formErrors.display_name}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="bio">Bio</Label>
|
||||
<Textarea
|
||||
id="bio"
|
||||
value={editForm.bio}
|
||||
onChange={(e) => setEditForm(prev => ({ ...prev, bio: e.target.value }))}
|
||||
placeholder="Tell us about yourself..."
|
||||
rows={3}
|
||||
className={formErrors.bio ? 'border-destructive' : ''}
|
||||
/>
|
||||
{formErrors.bio && (
|
||||
<p className="text-sm text-destructive mt-1">{formErrors.bio}</p>
|
||||
)}
|
||||
<Textarea id="bio" value={editForm.bio} onChange={e => setEditForm(prev => ({
|
||||
...prev,
|
||||
bio: e.target.value
|
||||
}))} placeholder="Tell us about yourself..." rows={3} className={formErrors.bio ? 'border-destructive' : ''} />
|
||||
{formErrors.bio && <p className="text-sm text-destructive mt-1">{formErrors.bio}</p>}
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
onClick={handleSaveProfile}
|
||||
size="sm"
|
||||
disabled={usernameValidation.isChecking || (editForm.username !== profile?.username && !usernameValidation.isValid)}
|
||||
>
|
||||
<Button onClick={handleSaveProfile} size="sm" disabled={usernameValidation.isChecking || editForm.username !== profile?.username && !usernameValidation.isValid}>
|
||||
<Save className="w-4 h-4 mr-2" />
|
||||
Save Changes
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setEditing(false);
|
||||
setFormErrors({});
|
||||
setEditForm({
|
||||
username: profile?.username || '',
|
||||
display_name: profile?.display_name || '',
|
||||
bio: profile?.bio || '',
|
||||
});
|
||||
}}
|
||||
size="sm"
|
||||
>
|
||||
<Button variant="outline" onClick={() => {
|
||||
setEditing(false);
|
||||
setFormErrors({});
|
||||
setEditForm({
|
||||
username: profile?.username || '',
|
||||
display_name: profile?.display_name || '',
|
||||
bio: profile?.bio || ''
|
||||
});
|
||||
}} size="sm">
|
||||
<X className="w-4 h-4 mr-2" />
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
</div> : <div>
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<h1 className="text-3xl font-bold">
|
||||
{profile.display_name || profile.username}
|
||||
</h1>
|
||||
{profile.display_name && (
|
||||
<Badge variant="secondary">@{profile.username}</Badge>
|
||||
)}
|
||||
{profile.display_name && <Badge variant="secondary">@{profile.username}</Badge>}
|
||||
</div>
|
||||
|
||||
{profile.bio && (
|
||||
<p className="text-muted-foreground mb-4 max-w-2xl">
|
||||
{profile.bio && <p className="text-muted-foreground mb-4 max-w-2xl">
|
||||
{profile.bio}
|
||||
</p>
|
||||
)}
|
||||
</p>}
|
||||
|
||||
<div className="flex flex-wrap gap-4 text-sm text-muted-foreground">
|
||||
<div className="flex items-center gap-1">
|
||||
<Calendar className="w-4 h-4" />
|
||||
Joined {new Date(profile.created_at).toLocaleDateString('en-US', {
|
||||
month: 'long',
|
||||
year: 'numeric'
|
||||
})}
|
||||
Joined {new Date(profile.created_at).toLocaleDateString('en-US', {
|
||||
month: 'long',
|
||||
year: 'numeric'
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Show pronouns if enabled */}
|
||||
{profile.show_pronouns && profile.preferred_pronouns && (
|
||||
<div className="flex items-center gap-1">
|
||||
{profile.show_pronouns && profile.preferred_pronouns && <div className="flex items-center gap-1">
|
||||
<User className="w-4 h-4" />
|
||||
{profile.preferred_pronouns}
|
||||
</div>
|
||||
)}
|
||||
</div>}
|
||||
|
||||
{/* Show location only if privacy allows */}
|
||||
{profile.location && (
|
||||
<LocationDisplay
|
||||
location={profile.location}
|
||||
userId={profile.user_id}
|
||||
isOwnProfile={isOwnProfile}
|
||||
/>
|
||||
)}
|
||||
{profile.location && <LocationDisplay location={profile.location} userId={profile.user_id} isOwnProfile={isOwnProfile} />}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -549,7 +435,7 @@ export default function Profile() {
|
||||
<TabsList className="grid w-full grid-cols-4">
|
||||
<TabsTrigger value="activity">Activity</TabsTrigger>
|
||||
<TabsTrigger value="reviews">Reviews</TabsTrigger>
|
||||
<TabsTrigger value="lists">Top Lists</TabsTrigger>
|
||||
<TabsTrigger value="lists">Rankings</TabsTrigger>
|
||||
<TabsTrigger value="credits">Ride Credits</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
@@ -596,10 +482,8 @@ export default function Profile() {
|
||||
<TabsContent value="lists" className="mt-6">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Top Lists</CardTitle>
|
||||
<CardDescription>
|
||||
Personal rankings and favorite collections
|
||||
</CardDescription>
|
||||
<CardTitle>Rankings</CardTitle>
|
||||
<CardDescription>Personal rankings of rides</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-center py-12">
|
||||
@@ -660,6 +544,5 @@ export default function Profile() {
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
);
|
||||
</div>;
|
||||
}
|
||||
Reference in New Issue
Block a user