page wrapper
This commit is contained in:
@@ -1,53 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { DriverLeaderboardItemViewModel } from '@/lib/view-models/DriverLeaderboardItemViewModel';
|
||||
import { DriversTemplate } from '@/templates/DriversTemplate';
|
||||
|
||||
// Shared state components
|
||||
import { StateContainer } from '@/components/shared/state/StateContainer';
|
||||
import { useDriverLeaderboard } from '@/hooks/driver/useDriverLeaderboard';
|
||||
import { Users } from 'lucide-react';
|
||||
|
||||
export function DriversInteractive() {
|
||||
|
||||
const { data: viewModel, isLoading: loading, error, retry } = useDriverLeaderboard();
|
||||
|
||||
const drivers = viewModel?.drivers || [];
|
||||
const totalRaces = viewModel?.totalRaces || 0;
|
||||
const totalWins = viewModel?.totalWins || 0;
|
||||
const activeCount = viewModel?.activeCount || 0;
|
||||
|
||||
// TODO this should not be done in a page, thats part of the service??
|
||||
// Transform data for template
|
||||
const driverViewModels = drivers.map((driver, index) =>
|
||||
new DriverLeaderboardItemViewModel(driver, index + 1)
|
||||
);
|
||||
|
||||
return (
|
||||
<StateContainer
|
||||
data={viewModel}
|
||||
isLoading={loading}
|
||||
error={error}
|
||||
retry={retry}
|
||||
config={{
|
||||
loading: { variant: 'skeleton', message: 'Loading driver leaderboard...' },
|
||||
error: { variant: 'inline' },
|
||||
empty: {
|
||||
icon: Users,
|
||||
title: 'No drivers found',
|
||||
description: 'There are no drivers in the system yet',
|
||||
}
|
||||
}}
|
||||
>
|
||||
{() => (
|
||||
<DriversTemplate
|
||||
drivers={driverViewModels}
|
||||
totalRaces={totalRaces}
|
||||
totalWins={totalWins}
|
||||
activeCount={activeCount}
|
||||
isLoading={false}
|
||||
/>
|
||||
)}
|
||||
</StateContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import { DriversTemplate } from '@/templates/DriversTemplate';
|
||||
import { DriverLeaderboardItemViewModel } from '@/lib/view-models/DriverLeaderboardItemViewModel';
|
||||
import { DriverLeaderboardViewModel } from '@/lib/view-models/DriverLeaderboardViewModel';
|
||||
|
||||
interface DriversStaticProps {
|
||||
leaderboardData: DriverLeaderboardViewModel;
|
||||
}
|
||||
|
||||
export async function DriversStatic({ leaderboardData }: DriversStaticProps) {
|
||||
// Transform the data for the template
|
||||
const drivers = leaderboardData.drivers.map((driver, index) =>
|
||||
new DriverLeaderboardItemViewModel(driver, index + 1)
|
||||
);
|
||||
|
||||
return (
|
||||
<DriversTemplate
|
||||
drivers={drivers}
|
||||
totalRaces={leaderboardData.totalRaces}
|
||||
totalWins={leaderboardData.totalWins}
|
||||
activeCount={leaderboardData.activeCount}
|
||||
isLoading={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { DriverProfileTemplate } from '@/templates/DriverProfileTemplate';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { DRIVER_SERVICE_TOKEN, TEAM_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import SponsorInsightsCard, { useSponsorMode, MetricBuilders, SlotTemplates } from '@/components/sponsors/SponsorInsightsCard';
|
||||
import { Car } from 'lucide-react';
|
||||
|
||||
interface Team {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface TeamMembershipInfo {
|
||||
team: Team;
|
||||
role: string;
|
||||
joinedAt: Date;
|
||||
}
|
||||
|
||||
export function DriverProfileInteractive() {
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const driverId = params.id as string;
|
||||
const driverService = useInject(DRIVER_SERVICE_TOKEN);
|
||||
const teamService = useInject(TEAM_SERVICE_TOKEN);
|
||||
|
||||
const [activeTab, setActiveTab] = useState<'overview' | 'stats'>('overview');
|
||||
const [friendRequestSent, setFriendRequestSent] = useState(false);
|
||||
|
||||
const isSponsorMode = useSponsorMode();
|
||||
|
||||
// Fetch driver profile using React-Query
|
||||
const { data: driverProfile, isLoading, error, refetch } = useQuery({
|
||||
queryKey: ['driverProfile', driverId],
|
||||
queryFn: () => driverService.getDriverProfile(driverId),
|
||||
});
|
||||
|
||||
// Fetch team memberships using React-Query
|
||||
const { data: allTeamMemberships } = useQuery({
|
||||
queryKey: ['driverTeamMemberships', driverId],
|
||||
queryFn: async () => {
|
||||
if (!driverProfile?.currentDriver) return [];
|
||||
|
||||
const allTeams = await teamService.getAllTeams();
|
||||
const memberships: TeamMembershipInfo[] = [];
|
||||
|
||||
for (const team of allTeams) {
|
||||
const teamMembers = await teamService.getTeamMembers(team.id, driverId, '');
|
||||
const membership = teamMembers.find(member => member.driverId === driverId);
|
||||
if (membership) {
|
||||
memberships.push({
|
||||
team: {
|
||||
id: team.id,
|
||||
name: team.name,
|
||||
} as Team,
|
||||
role: membership.role,
|
||||
joinedAt: new Date(membership.joinedAt),
|
||||
});
|
||||
}
|
||||
}
|
||||
return memberships;
|
||||
},
|
||||
enabled: !!driverProfile?.currentDriver,
|
||||
});
|
||||
|
||||
const handleAddFriend = () => {
|
||||
setFriendRequestSent(true);
|
||||
};
|
||||
|
||||
const handleBackClick = () => {
|
||||
router.push('/drivers');
|
||||
};
|
||||
|
||||
// Build sponsor insights for driver
|
||||
const friendsCount = driverProfile?.socialSummary?.friends?.length ?? 0;
|
||||
const stats = driverProfile?.stats || null;
|
||||
const driver = driverProfile?.currentDriver;
|
||||
|
||||
const driverMetrics = [
|
||||
MetricBuilders.rating(stats?.rating ?? 0, 'Driver Rating'),
|
||||
MetricBuilders.views((friendsCount * 8) + 50),
|
||||
MetricBuilders.engagement(stats?.consistency ?? 75),
|
||||
MetricBuilders.reach((friendsCount * 12) + 100),
|
||||
];
|
||||
|
||||
const sponsorInsights = isSponsorMode && driver ? (
|
||||
<SponsorInsightsCard
|
||||
entityType="driver"
|
||||
entityId={driver.id}
|
||||
entityName={driver.name}
|
||||
tier="standard"
|
||||
metrics={driverMetrics}
|
||||
slots={SlotTemplates.driver(true, 200)}
|
||||
trustScore={88}
|
||||
monthlyActivity={stats?.consistency ?? 75}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
// Loading state
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen">
|
||||
<div className="text-center">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
|
||||
<p className="text-gray-600">Loading driver profile...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Error state
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen">
|
||||
<div className="text-center max-w-md">
|
||||
<div className="text-red-600 text-4xl mb-4">⚠️</div>
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-2">Error loading driver profile</h2>
|
||||
<p className="text-gray-600 mb-4">{error.message}</p>
|
||||
<button
|
||||
onClick={() => refetch()}
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
|
||||
>
|
||||
Retry
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Empty state
|
||||
if (!driverProfile) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen">
|
||||
<div className="text-center max-w-md">
|
||||
<div className="text-gray-400 text-4xl mb-4">
|
||||
<Car size={48} />
|
||||
</div>
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-2">Driver not found</h2>
|
||||
<p className="text-gray-600 mb-4">The driver profile may not exist or you may not have access</p>
|
||||
<button
|
||||
onClick={handleBackClick}
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
|
||||
>
|
||||
Back to Drivers
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DriverProfileTemplate
|
||||
driverProfile={driverProfile}
|
||||
allTeamMemberships={allTeamMemberships || []}
|
||||
isLoading={false}
|
||||
error={null}
|
||||
onBackClick={handleBackClick}
|
||||
onAddFriend={handleAddFriend}
|
||||
friendRequestSent={friendRequestSent}
|
||||
activeTab={activeTab}
|
||||
setActiveTab={setActiveTab}
|
||||
isSponsorMode={isSponsorMode}
|
||||
sponsorInsights={sponsorInsights}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import { DriverProfileTemplate } from '@/templates/DriverProfileTemplate';
|
||||
import { DriverProfileViewModel } from '@/lib/view-models/DriverProfileViewModel';
|
||||
|
||||
interface DriverProfileStaticProps {
|
||||
profileData: DriverProfileViewModel;
|
||||
teamMemberships: Array<{
|
||||
team: { id: string; name: string };
|
||||
role: string;
|
||||
joinedAt: Date;
|
||||
}>;
|
||||
}
|
||||
|
||||
export async function DriverProfileStatic({ profileData, teamMemberships }: DriverProfileStaticProps) {
|
||||
return (
|
||||
<DriverProfileTemplate
|
||||
driverProfile={profileData}
|
||||
allTeamMemberships={teamMemberships}
|
||||
isLoading={false}
|
||||
error={null}
|
||||
onBackClick={() => {
|
||||
// This will be handled by the parent page component
|
||||
window.history.back();
|
||||
}}
|
||||
onAddFriend={() => {
|
||||
// Server component - no-op for static version
|
||||
console.log('Add friend - static mode');
|
||||
}}
|
||||
friendRequestSent={false}
|
||||
activeTab="overview"
|
||||
setActiveTab={() => {
|
||||
// Server component - no-op for static version
|
||||
console.log('Set tab - static mode');
|
||||
}}
|
||||
isSponsorMode={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,81 @@
|
||||
import { DriverProfileInteractive } from './DriverProfileInteractive';
|
||||
'use client';
|
||||
|
||||
export default DriverProfileInteractive;
|
||||
import { StatefulPageWrapper } from '@/components/shared/state/StatefulPageWrapper';
|
||||
import { DriverProfileTemplate } from '@/templates/DriverProfileTemplate';
|
||||
import { useDriverProfilePageData } from '@/hooks/driver/useDriverProfilePageData';
|
||||
import { useEffectiveDriverId } from '@/hooks/useEffectiveDriverId';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
|
||||
interface DriverProfileData {
|
||||
driverProfile: any;
|
||||
teamMemberships: Array<{
|
||||
team: { id: string; name: string };
|
||||
role: string;
|
||||
joinedAt: Date;
|
||||
}>;
|
||||
}
|
||||
|
||||
export default function DriverProfilePage() {
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const driverId = params.id as string;
|
||||
const currentDriverId = useEffectiveDriverId() || '';
|
||||
|
||||
// UI State
|
||||
const [activeTab, setActiveTab] = useState<'overview' | 'stats'>('overview');
|
||||
const [friendRequestSent, setFriendRequestSent] = useState(false);
|
||||
|
||||
// Fetch data using domain hook
|
||||
const { data: queries, isLoading, error, refetch } = useDriverProfilePageData(driverId);
|
||||
|
||||
// Transform data for template
|
||||
const data: DriverProfileData | undefined = queries?.driverProfile && queries?.teamMemberships
|
||||
? {
|
||||
driverProfile: queries.driverProfile,
|
||||
teamMemberships: queries.teamMemberships,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
// Actions
|
||||
const handleAddFriend = () => {
|
||||
setFriendRequestSent(true);
|
||||
};
|
||||
|
||||
const handleBackClick = () => {
|
||||
router.push('/drivers');
|
||||
};
|
||||
|
||||
return (
|
||||
<StatefulPageWrapper
|
||||
data={data}
|
||||
isLoading={isLoading}
|
||||
error={error as Error | null}
|
||||
retry={refetch}
|
||||
Template={({ data }) => {
|
||||
if (!data) return null;
|
||||
return (
|
||||
<DriverProfileTemplate
|
||||
driverProfile={data.driverProfile}
|
||||
allTeamMemberships={data.teamMemberships}
|
||||
isLoading={false}
|
||||
error={null}
|
||||
onBackClick={handleBackClick}
|
||||
onAddFriend={handleAddFriend}
|
||||
friendRequestSent={friendRequestSent}
|
||||
activeTab={activeTab}
|
||||
setActiveTab={setActiveTab}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
loading={{ variant: 'skeleton', message: 'Loading driver profile...' }}
|
||||
errorConfig={{ variant: 'full-screen' }}
|
||||
empty={{
|
||||
icon: require('lucide-react').Car,
|
||||
title: 'Driver not found',
|
||||
description: 'The driver profile may not exist or you may not have access',
|
||||
action: { label: 'Back to Drivers', onClick: handleBackClick }
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,14 @@
|
||||
import { DriversInteractive } from './DriversInteractive';
|
||||
import { PageWrapper } from '@/components/shared/state/PageWrapper';
|
||||
import { DriversTemplate } from '@/templates/DriversTemplate';
|
||||
import { PageDataFetcher } from '@/lib/page/PageDataFetcher';
|
||||
import { DRIVER_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import type { DriverService } from '@/lib/services/drivers/DriverService';
|
||||
|
||||
export default DriversInteractive;
|
||||
export default async function Page() {
|
||||
const data = await PageDataFetcher.fetch<DriverService, 'getDriverLeaderboard'>(
|
||||
DRIVER_SERVICE_TOKEN,
|
||||
'getDriverLeaderboard'
|
||||
);
|
||||
|
||||
return <PageWrapper data={data} Template={DriversTemplate} />;
|
||||
}
|
||||
Reference in New Issue
Block a user