Refactor: Implement complete plan

This commit is contained in:
gpt-engineer-app[bot]
2025-10-17 14:04:57 +00:00
parent a89a740611
commit a2e05c5080
10 changed files with 258 additions and 68 deletions

View File

@@ -19,7 +19,7 @@ import { UserListManager } from '@/components/lists/UserListManager';
import { RideCreditsManager } from '@/components/profile/RideCreditsManager';
import { useUsernameValidation } from '@/hooks/useUsernameValidation';
import { User, MapPin, Calendar, Star, Trophy, Settings, Camera, Edit3, Save, X, ArrowLeft, Check, AlertCircle, Loader2, UserX, FileText, Image } from 'lucide-react';
import { Profile as ProfileType, ActivityEntry, ReviewActivity, SubmissionActivity, RankingActivity } from '@/types/database';
import { Profile as ProfileType } from '@/types/database';
import { supabase } from '@/integrations/supabase/client';
import { useToast } from '@/hooks/use-toast';
import { getErrorMessage } from '@/lib/errorHandler';
@@ -30,6 +30,95 @@ import { UserBlockButton } from '@/components/profile/UserBlockButton';
import { PersonalLocationDisplay } from '@/components/profile/PersonalLocationDisplay';
import { useUserRole } from '@/hooks/useUserRole';
// Activity type definitions
interface SubmissionActivity {
id: string;
type: 'submission';
submission_type: 'park' | 'ride' | 'photo' | 'company' | 'ride_model';
status: string;
created_at: string;
content?: {
action?: 'edit' | 'create';
name?: string;
slug?: string;
entity_slug?: string;
entity_name?: string;
park_slug?: string;
park_name?: string;
company_type?: string;
manufacturer_slug?: string;
description?: string;
};
photo_preview?: string;
photo_count?: number;
entity_type?: 'park' | 'ride' | 'company';
entity_id?: string;
}
interface RankingActivity {
id: string;
type: 'ranking';
title: string;
description?: string;
list_type: string;
created_at: string;
}
interface ReviewActivity {
id: string;
type: 'review';
rating: number;
title?: string;
content?: string;
created_at: string;
moderation_status?: string;
park_id?: string;
ride_id?: string;
parks?: {
name: string;
slug: string;
} | null;
rides?: {
name: string;
slug: string;
parks?: {
name: string;
slug: string;
} | null;
} | null;
}
interface CreditActivity {
id: string;
type: 'credit';
ride_count: number;
first_ride_date?: string;
created_at: string;
rides?: {
name: string;
slug: string;
parks?: {
name: string;
slug: string;
} | null;
} | null;
}
type ActivityEntry = SubmissionActivity | RankingActivity | ReviewActivity | CreditActivity;
// Type guards
const isSubmissionActivity = (act: ActivityEntry): act is SubmissionActivity =>
act.type === 'submission';
const isRankingActivity = (act: ActivityEntry): act is RankingActivity =>
act.type === 'ranking';
const isReviewActivity = (act: ActivityEntry): act is ReviewActivity =>
act.type === 'review';
const isCreditActivity = (act: ActivityEntry): act is CreditActivity =>
act.type === 'credit';
export default function Profile() {
const {
username
@@ -764,7 +853,7 @@ export default function Profile() {
<>
<div className="flex items-center gap-2 mb-1">
<p className="font-medium">
{reviewActivity.title || reviewActivity.description || 'Left a review'}
{reviewActivity.title || reviewActivity.content || 'Left a review'}
</p>
</div>
<div className="flex items-center gap-1 mb-2">
@@ -789,58 +878,58 @@ export default function Profile() {
</>
);
})()
) : activity.type === 'submission' ? (
) : isSubmissionActivity(activity) ? (
<>
<div className="flex items-center gap-2 mb-1">
<p className="font-medium">
{(activity as any).content?.action === 'edit' ? 'Edited' : 'Submitted'}{' '}
{(activity as any).submission_type === 'photo' ? 'photos for' : (activity as any).submission_type || 'content'}
{(activity as any).content?.name && ` ${(activity as any).content.name}`}
{activity.content?.action === 'edit' ? 'Edited' : 'Submitted'}{' '}
{activity.submission_type === 'photo' ? 'photos for' : activity.submission_type || 'content'}
{activity.content?.name && ` ${activity.content.name}`}
</p>
{(activity as any).status === 'pending' && (
{activity.status === 'pending' && (
<Badge variant="secondary" className="text-xs">Pending</Badge>
)}
{(activity as any).status === 'approved' && (
{activity.status === 'approved' && (
<Badge variant="default" className="text-xs">Approved</Badge>
)}
{(activity as any).status === 'rejected' && (
{activity.status === 'rejected' && (
<Badge variant="destructive" className="text-xs">Rejected</Badge>
)}
{(activity as any).status === 'partially_approved' && (
{activity.status === 'partially_approved' && (
<Badge variant="outline" className="text-xs">Partially Approved</Badge>
)}
</div>
{/* Photo preview for photo submissions */}
{(activity as any).submission_type === 'photo' && (activity as any).photo_preview && (
{activity.submission_type === 'photo' && activity.photo_preview && (
<div className="flex gap-2 items-center mb-2">
<img
src={(activity as any).photo_preview}
src={activity.photo_preview}
alt="Photo preview"
className="w-16 h-16 rounded object-cover border"
/>
{(activity as any).photo_count > 1 && (
{activity.photo_count && activity.photo_count > 1 && (
<span className="text-xs text-muted-foreground">
+{(activity as any).photo_count - 1} more
+{activity.photo_count - 1} more
</span>
)}
</div>
)}
{/* Entity link for photo submissions */}
{(activity as any).submission_type === 'photo' && (activity as any).content?.entity_slug && (
{activity.submission_type === 'photo' && activity.content?.entity_slug && (
<div className="text-sm text-muted-foreground">
{(activity as any).entity_type === 'park' ? (
<Link to={`/parks/${(activity as any).content.entity_slug}`} className="hover:text-accent transition-colors">
{(activity as any).content.entity_name || 'View park'}
{activity.entity_type === 'park' ? (
<Link to={`/parks/${activity.content.entity_slug}`} className="hover:text-accent transition-colors">
{activity.content.entity_name || 'View park'}
</Link>
) : (activity as any).entity_type === 'ride' ? (
) : activity.entity_type === 'ride' ? (
<>
<Link to={`/parks/${(activity as any).content.park_slug}/rides/${(activity as any).content.entity_slug}`} className="hover:text-accent transition-colors">
{(activity as any).content.entity_name || 'View ride'}
<Link to={`/parks/${activity.content.park_slug}/rides/${activity.content.entity_slug}`} className="hover:text-accent transition-colors">
{activity.content.entity_name || 'View ride'}
</Link>
{(activity as any).content.park_name && (
<span className="text-muted-foreground/70"> at {(activity as any).content.park_name}</span>
{activity.content.park_name && (
<span className="text-muted-foreground/70"> at {activity.content.park_name}</span>
)}
</>
) : null}
@@ -848,42 +937,42 @@ export default function Profile() {
)}
{/* Links for entity submissions */}
{(activity as any).status === 'approved' && (activity as any).submission_type !== 'photo' && (
{activity.status === 'approved' && activity.submission_type !== 'photo' && (
<>
{(activity as any).submission_type === 'park' && (activity as any).content?.slug && (
{activity.submission_type === 'park' && activity.content?.slug && (
<Link
to={`/parks/${(activity as any).content.slug}`}
to={`/parks/${activity.content.slug}`}
className="text-sm text-accent hover:underline"
>
View park
</Link>
)}
{(activity as any).submission_type === 'ride' && (activity as any).content?.slug && (activity as any).content?.park_slug && (
{activity.submission_type === 'ride' && activity.content?.slug && activity.content?.park_slug && (
<div className="text-sm">
<Link
to={`/parks/${(activity as any).content.park_slug}/rides/${(activity as any).content.slug}`}
to={`/parks/${activity.content.park_slug}/rides/${activity.content.slug}`}
className="text-accent hover:underline"
>
View ride
</Link>
{(activity as any).content.park_name && (
{activity.content.park_name && (
<span className="text-muted-foreground ml-1">
at {(activity as any).content.park_name}
at {activity.content.park_name}
</span>
)}
</div>
)}
{(activity as any).submission_type === 'company' && (activity as any).content?.slug && (
{activity.submission_type === 'company' && activity.content?.slug && (
<Link
to={`/${(activity as any).content.company_type === 'operator' ? 'operators' : (activity as any).content.company_type === 'property_owner' ? 'owners' : (activity as any).content.company_type === 'manufacturer' ? 'manufacturers' : 'designers'}/${(activity as any).content.slug}`}
to={`/${activity.content.company_type === 'operator' ? 'operators' : activity.content.company_type === 'property_owner' ? 'owners' : activity.content.company_type === 'manufacturer' ? 'manufacturers' : 'designers'}/${activity.content.slug}`}
className="text-sm text-accent hover:underline"
>
View {(activity as any).content.company_type || 'company'}
View {activity.content.company_type || 'company'}
</Link>
)}
{(activity as any).submission_type === 'ride_model' && (activity as any).content?.slug && (activity as any).content?.manufacturer_slug && (
{activity.submission_type === 'ride_model' && activity.content?.slug && activity.content?.manufacturer_slug && (
<Link
to={`/manufacturers/${(activity as any).content.manufacturer_slug}/models/${(activity as any).content.slug}`}
to={`/manufacturers/${activity.content.manufacturer_slug}/models/${activity.content.slug}`}
className="text-sm text-accent hover:underline"
>
View model
@@ -892,54 +981,51 @@ export default function Profile() {
</>
)}
{(activity as any).content?.description && (activity as any).submission_type !== 'photo' && (
{activity.content?.description && activity.submission_type !== 'photo' && (
<p className="text-sm text-muted-foreground line-clamp-2">
{(activity as any).content.description}
{activity.content.description}
</p>
)}
</>
) : activity.type === 'ranking' ? (
) : isRankingActivity(activity) ? (
<>
<div className="flex items-center gap-2 mb-1">
<Link
to={`/profile/${profile?.username}/lists`}
className="font-medium hover:text-accent transition-colors"
>
Created ranking: {(activity as any).title || 'Untitled'}
Created ranking: {activity.title || 'Untitled'}
</Link>
<Badge variant="outline" className="text-xs capitalize">
{((activity as any).list_type || '').replace('_', ' ')}
{(activity.list_type || '').replace('_', ' ')}
</Badge>
{(activity as any).is_public === false && (
<Badge variant="secondary" className="text-xs">Private</Badge>
)}
</div>
{(activity as any).description && (
{activity.description && (
<p className="text-sm text-muted-foreground line-clamp-2">
{(activity as any).description}
{activity.description}
</p>
)}
</>
) : (
) : isCreditActivity(activity) ? (
<>
<p className="font-medium mb-1">Added ride credit</p>
{(activity as any).rides && (
{activity.rides && (
<div className="text-sm text-muted-foreground">
<Link to={`/parks/${(activity as any).rides.parks?.slug}/rides/${(activity as any).rides.slug}`} className="hover:text-accent transition-colors">
{(activity as any).rides.name}
<Link to={`/parks/${activity.rides.parks?.slug}/rides/${activity.rides.slug}`} className="hover:text-accent transition-colors">
{activity.rides.name}
</Link>
{(activity as any).rides.parks && (
<span className="text-muted-foreground/70"> at {(activity as any).rides.parks.name}</span>
{activity.rides.parks && (
<span className="text-muted-foreground/70"> at {activity.rides.parks.name}</span>
)}
</div>
)}
{(activity as any).ride_count > 1 && (
{activity.ride_count > 1 && (
<p className="text-xs text-muted-foreground mt-1">
Ridden {(activity as any).ride_count} times
Ridden {activity.ride_count} times
</p>
)}
</>
)}
) : null}
</div>
<div className="flex-shrink-0 text-xs text-muted-foreground">