remove core from pages
This commit is contained in:
@@ -3,13 +3,14 @@
|
||||
import Button from '@/components/ui/Button';
|
||||
import Card from '@/components/ui/Card';
|
||||
import { useEffectiveDriverId } from '@/hooks/useEffectiveDriverId';
|
||||
import type { League } from '@core/racing/domain/entities/League';
|
||||
import type { LeagueMembership } from '@core/racing/domain/entities/LeagueMembership';
|
||||
import { useServices } from '@/lib/services/ServiceProvider';
|
||||
import type { LeagueMembership } from '@/lib/types/LeagueMembership';
|
||||
import type { LeagueSummaryViewModel } from '@/lib/view-models/LeagueSummaryViewModel';
|
||||
import Link from 'next/link';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
interface LeagueWithRole {
|
||||
league: League;
|
||||
league: LeagueSummaryViewModel;
|
||||
membership: LeagueMembership;
|
||||
}
|
||||
|
||||
@@ -19,6 +20,7 @@ export default function ManageLeaguesPage() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const effectiveDriverId = useEffectiveDriverId();
|
||||
const { leagueService, leagueMembershipService } = useServices();
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
@@ -26,14 +28,12 @@ export default function ManageLeaguesPage() {
|
||||
const load = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const leagueRepo = getLeagueRepository();
|
||||
const membershipRepo = getLeagueMembershipRepository();
|
||||
|
||||
const leagues = await leagueRepo.findAll();
|
||||
const leagues = await leagueService.getAllLeagues();
|
||||
|
||||
const memberships = await Promise.all(
|
||||
leagues.map(async (league) => {
|
||||
const membership = await membershipRepo.getMembership(league.id, effectiveDriverId);
|
||||
await leagueMembershipService.fetchLeagueMemberships(league.id);
|
||||
const membership = leagueMembershipService.getMembership(league.id, effectiveDriverId);
|
||||
return { league, membership };
|
||||
}),
|
||||
);
|
||||
@@ -76,7 +76,7 @@ export default function ManageLeaguesPage() {
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [effectiveDriverId]);
|
||||
}, [effectiveDriverId, leagueService, leagueMembershipService]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
|
||||
@@ -7,12 +7,12 @@ import Button from '@/components/ui/Button';
|
||||
import Card from '@/components/ui/Card';
|
||||
import Heading from '@/components/ui/Heading';
|
||||
import { useEffectiveDriverId } from '@/hooks/useEffectiveDriverId';
|
||||
import type { DriverDTO } from '@core/racing/application/dto/DriverDTO';
|
||||
import { useServices } from '@/lib/services/ServiceProvider';
|
||||
import type {
|
||||
ProfileOverviewAchievementViewModel,
|
||||
ProfileOverviewSocialHandleViewModel,
|
||||
ProfileOverviewViewModel
|
||||
} from '@/lib/view-models/ProfileOverviewViewModel';
|
||||
DriverProfileAchievementViewModel,
|
||||
DriverProfileSocialHandleViewModel,
|
||||
DriverProfileViewModel
|
||||
} from '@/lib/view-models/DriverProfileViewModel';
|
||||
import {
|
||||
Activity,
|
||||
Award,
|
||||
@@ -67,7 +67,7 @@ function getCountryFlag(countryCode: string): string {
|
||||
return '🏁';
|
||||
}
|
||||
|
||||
function getRarityColor(rarity: ProfileOverviewAchievementViewModel['rarity']) {
|
||||
function getRarityColor(rarity: DriverProfileAchievementViewModel['rarity']) {
|
||||
switch (rarity) {
|
||||
case 'common':
|
||||
return 'text-gray-400 bg-gray-400/10 border-gray-400/30';
|
||||
@@ -80,7 +80,7 @@ function getRarityColor(rarity: ProfileOverviewAchievementViewModel['rarity']) {
|
||||
}
|
||||
}
|
||||
|
||||
function getAchievementIcon(icon: ProfileOverviewAchievementViewModel['icon']) {
|
||||
function getAchievementIcon(icon: DriverProfileAchievementViewModel['icon']) {
|
||||
switch (icon) {
|
||||
case 'trophy':
|
||||
return Trophy;
|
||||
@@ -97,7 +97,7 @@ function getAchievementIcon(icon: ProfileOverviewAchievementViewModel['icon']) {
|
||||
}
|
||||
}
|
||||
|
||||
function getSocialIcon(platform: ProfileOverviewSocialHandleViewModel['platform']) {
|
||||
function getSocialIcon(platform: DriverProfileSocialHandleViewModel['platform']) {
|
||||
switch (platform) {
|
||||
case 'twitter':
|
||||
return Twitter;
|
||||
@@ -110,7 +110,7 @@ function getSocialIcon(platform: ProfileOverviewSocialHandleViewModel['platform'
|
||||
}
|
||||
}
|
||||
|
||||
function getSocialColor(platform: ProfileOverviewSocialHandleViewModel['platform']) {
|
||||
function getSocialColor(platform: DriverProfileSocialHandleViewModel['platform']) {
|
||||
switch (platform) {
|
||||
case 'twitter':
|
||||
return 'hover:text-sky-400 hover:bg-sky-400/10';
|
||||
@@ -256,12 +256,13 @@ export default function ProfilePage() {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const tabParam = searchParams.get('tab') as ProfileTab | null;
|
||||
|
||||
const [driver, setDriver] = useState<DriverDTO | null>(null);
|
||||
|
||||
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 [profileData, setProfileData] = useState<ProfileOverviewViewModel | null>(null);
|
||||
const [friendRequestSent, setFriendRequestSent] = useState(false);
|
||||
|
||||
const effectiveDriverId = useEffectiveDriverId();
|
||||
@@ -271,24 +272,8 @@ export default function ProfilePage() {
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const currentDriverId = effectiveDriverId;
|
||||
|
||||
// Use GetProfileOverviewUseCase to load all profile data
|
||||
const profileUseCase = getGetProfileOverviewUseCase();
|
||||
const profileViewModel = await profileUseCase.execute({ driverId: currentDriverId });
|
||||
|
||||
if (profileViewModel && profileViewModel.currentDriver) {
|
||||
// Set driver from ViewModel instead of direct repository access
|
||||
const driverData: DriverDTO = {
|
||||
id: profileViewModel.currentDriver.id,
|
||||
name: profileViewModel.currentDriver.name,
|
||||
iracingId: profileViewModel.currentDriver.iracingId ?? '',
|
||||
country: profileViewModel.currentDriver.country,
|
||||
bio: profileViewModel.currentDriver.bio || '',
|
||||
joinedAt: profileViewModel.currentDriver.joinedAt,
|
||||
};
|
||||
setDriver(driverData);
|
||||
setProfileData(profileViewModel);
|
||||
}
|
||||
const profileViewModel = await driverService.getDriverProfile(currentDriverId);
|
||||
setProfileData(profileViewModel);
|
||||
} catch (error) {
|
||||
console.error('Failed to load profile:', error);
|
||||
} finally {
|
||||
@@ -296,7 +281,7 @@ export default function ProfilePage() {
|
||||
}
|
||||
};
|
||||
void loadData();
|
||||
}, [effectiveDriverId]);
|
||||
}, [effectiveDriverId, driverService]);
|
||||
|
||||
// Update URL when tab changes
|
||||
useEffect(() => {
|
||||
@@ -319,24 +304,13 @@ export default function ProfilePage() {
|
||||
}
|
||||
}, [tabParam]);
|
||||
|
||||
const handleSaveSettings = async (updates: Partial<DriverDTO>) => {
|
||||
if (!driver) return;
|
||||
const handleSaveSettings = async (updates: { bio?: string; country?: string }) => {
|
||||
if (!profileData?.currentDriver) return;
|
||||
|
||||
try {
|
||||
const updateProfileUseCase = getUpdateDriverProfileUseCase();
|
||||
const input: { driverId: string; bio?: string; country?: string } = { driverId: driver.id };
|
||||
if (typeof updates.bio === 'string') {
|
||||
input.bio = updates.bio;
|
||||
}
|
||||
if (typeof updates.country === 'string') {
|
||||
input.country = updates.country;
|
||||
}
|
||||
const updatedDto = await updateProfileUseCase.execute(input);
|
||||
|
||||
if (updatedDto) {
|
||||
setDriver(updatedDto);
|
||||
setEditMode(false);
|
||||
}
|
||||
const updatedProfile = await driverService.updateProfile(updates);
|
||||
setProfileData(updatedProfile);
|
||||
setEditMode(false);
|
||||
} catch (error) {
|
||||
console.error('Failed to update profile:', error);
|
||||
}
|
||||
@@ -360,7 +334,7 @@ export default function ProfilePage() {
|
||||
);
|
||||
}
|
||||
|
||||
if (!driver) {
|
||||
if (!profileData?.currentDriver) {
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-4">
|
||||
<div className="text-center mb-8">
|
||||
@@ -387,12 +361,12 @@ export default function ProfilePage() {
|
||||
}
|
||||
|
||||
// Extract data from profileData ViewModel
|
||||
const currentDriver = profileData?.currentDriver || null;
|
||||
const stats = profileData?.stats || null;
|
||||
const finishDistribution = profileData?.finishDistribution || null;
|
||||
const teamMemberships = profileData?.teamMemberships || [];
|
||||
const socialSummary = profileData?.socialSummary || { friendsCount: 0, friends: [] };
|
||||
const extendedProfile = profileData?.extendedProfile;
|
||||
const currentDriver = profileData.currentDriver;
|
||||
const stats = profileData.stats;
|
||||
const finishDistribution = profileData.finishDistribution;
|
||||
const teamMemberships = profileData.teamMemberships;
|
||||
const socialSummary = profileData.socialSummary;
|
||||
const extendedProfile = profileData.extendedProfile;
|
||||
const globalRank = currentDriver?.globalRank || null;
|
||||
|
||||
// Show edit mode
|
||||
@@ -405,7 +379,7 @@ export default function ProfilePage() {
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
<ProfileSettings driver={driver} onSave={handleSaveSettings} />
|
||||
<ProfileSettings driver={profileData.currentDriver} onSave={handleSaveSettings} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -428,8 +402,8 @@ export default function ProfilePage() {
|
||||
<div className="w-28 h-28 md:w-36 md:h-36 rounded-2xl bg-gradient-to-br from-primary-blue to-purple-600 p-1 shadow-xl shadow-primary-blue/20">
|
||||
<div className="w-full h-full rounded-xl overflow-hidden bg-iron-gray">
|
||||
<Image
|
||||
src={getImageService().getDriverAvatar(driver.id)}
|
||||
alt={driver.name}
|
||||
src={mediaService.getDriverAvatar(currentDriver.id)}
|
||||
alt={currentDriver.name}
|
||||
width={144}
|
||||
height={144}
|
||||
className="w-full h-full object-cover"
|
||||
@@ -443,9 +417,9 @@ export default function ProfilePage() {
|
||||
{/* Driver Info */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex flex-wrap items-center gap-3 mb-2">
|
||||
<h1 className="text-3xl md:text-4xl font-bold text-white">{driver.name}</h1>
|
||||
<span className="text-4xl" aria-label={`Country: ${driver.country}`}>
|
||||
{getCountryFlag(driver.country)}
|
||||
<h1 className="text-3xl md:text-4xl font-bold text-white">{currentDriver.name}</h1>
|
||||
<span className="text-4xl" aria-label={`Country: ${currentDriver.country}`}>
|
||||
{getCountryFlag(currentDriver.country)}
|
||||
</span>
|
||||
{teamMemberships.length > 0 && teamMemberships[0] && (
|
||||
<span className="px-3 py-1 bg-purple-600/20 text-purple-400 rounded-full text-sm font-semibold border border-purple-600/30">
|
||||
@@ -488,11 +462,11 @@ export default function ProfilePage() {
|
||||
<div className="flex flex-wrap items-center gap-4 text-sm text-gray-400">
|
||||
<span className="flex items-center gap-1.5">
|
||||
<Globe className="w-4 h-4" />
|
||||
iRacing: {driver.iracingId}
|
||||
iRacing: {currentDriver.iracingId}
|
||||
</span>
|
||||
<span className="flex items-center gap-1.5">
|
||||
<Calendar className="w-4 h-4" />
|
||||
Joined {new Date(driver.joinedAt).toLocaleDateString('en-US', { month: 'short', year: 'numeric' })}
|
||||
Joined {new Date(currentDriver.joinedAt).toLocaleDateString('en-US', { month: 'short', year: 'numeric' })}
|
||||
</span>
|
||||
{extendedProfile && (
|
||||
<span className="flex items-center gap-1.5">
|
||||
@@ -564,13 +538,13 @@ export default function ProfilePage() {
|
||||
</div>
|
||||
|
||||
{/* Bio Section */}
|
||||
{driver.bio && (
|
||||
{currentDriver.bio && (
|
||||
<Card>
|
||||
<h2 className="text-lg font-semibold text-white mb-3 flex items-center gap-2">
|
||||
<User className="w-5 h-5 text-primary-blue" />
|
||||
About
|
||||
</h2>
|
||||
<p className="text-gray-300 leading-relaxed">{driver.bio}</p>
|
||||
<p className="text-gray-300 leading-relaxed">{currentDriver.bio}</p>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
@@ -910,7 +884,7 @@ export default function ProfilePage() {
|
||||
>
|
||||
<div className="w-8 h-8 rounded-full overflow-hidden bg-gradient-to-br from-primary-blue to-purple-600">
|
||||
<Image
|
||||
src={getImageService().getDriverAvatar(friend.id)}
|
||||
src={mediaService.getDriverAvatar(friend.id)}
|
||||
alt={friend.name}
|
||||
width={32}
|
||||
height={32}
|
||||
@@ -932,13 +906,13 @@ export default function ProfilePage() {
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeTab === 'history' && driver && (
|
||||
{activeTab === 'history' && currentDriver && (
|
||||
<Card>
|
||||
<h2 className="text-lg font-semibold text-white mb-4 flex items-center gap-2">
|
||||
<History className="w-5 h-5 text-red-400" />
|
||||
Race History
|
||||
</h2>
|
||||
<ProfileRaceHistory driverId={driver.id} />
|
||||
<ProfileRaceHistory driverId={currentDriver.id} />
|
||||
</Card>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
'use client';
|
||||
|
||||
import Breadcrumbs from '@/components/layout/Breadcrumbs';
|
||||
import PendingSponsorshipRequests, { type PendingRequestDTO } from '@/components/sponsors/PendingSponsorshipRequests';
|
||||
import PendingSponsorshipRequests from '@/components/sponsors/PendingSponsorshipRequests';
|
||||
import Button from '@/components/ui/Button';
|
||||
import Card from '@/components/ui/Card';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { useEffectiveDriverId } from '@/hooks/useEffectiveDriverId';
|
||||
import { isLeagueAdminOrHigherRole } from '@/lib/leagueRoles';
|
||||
import { LeagueRoleUtility } from '@/lib/utilities/LeagueRoleUtility';
|
||||
import { useServices } from '@/lib/services/ServiceProvider';
|
||||
import { SponsorshipRequestViewModel } from '@/lib/view-models/SponsorshipRequestViewModel';
|
||||
import { AlertTriangle, Building, ChevronRight, Handshake, Trophy, User, Users } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
@@ -17,7 +18,7 @@ interface EntitySection {
|
||||
entityType: 'driver' | 'team' | 'race' | 'season';
|
||||
entityId: string;
|
||||
entityName: string;
|
||||
requests: PendingRequestDTO[];
|
||||
requests: SponsorshipRequestViewModel[];
|
||||
}
|
||||
|
||||
export default function SponsorshipRequestsPage() {
|
||||
@@ -33,50 +34,45 @@ export default function SponsorshipRequestsPage() {
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const { sponsorshipService } = useServices();
|
||||
const driverRepo = getDriverRepository();
|
||||
const leagueRepo = getLeagueRepository();
|
||||
const teamRepo = getTeamRepository();
|
||||
const leagueMembershipRepo = getLeagueMembershipRepository();
|
||||
const teamMembershipRepo = getTeamMembershipRepository();
|
||||
|
||||
const { sponsorshipService, driverService, leagueService, teamService, leagueMembershipService } = useServices();
|
||||
|
||||
const allSections: EntitySection[] = [];
|
||||
|
||||
|
||||
// 1. Driver's own sponsorship requests
|
||||
const driverResult = await sponsorshipService.getPendingSponsorshipRequests({
|
||||
const driverRequests = await sponsorshipService.getPendingSponsorshipRequests({
|
||||
entityType: 'driver',
|
||||
entityId: currentDriverId,
|
||||
});
|
||||
|
||||
if (driverResult && driverResult.requests.length > 0) {
|
||||
const driver = await driverRepo.findById(currentDriverId);
|
||||
|
||||
if (driverRequests.length > 0) {
|
||||
const driverProfile = await driverService.getDriverProfile(currentDriverId);
|
||||
allSections.push({
|
||||
entityType: 'driver',
|
||||
entityId: currentDriverId,
|
||||
entityName: driver?.name ?? 'Your Profile',
|
||||
requests: driverResult.requests,
|
||||
entityName: driverProfile?.currentDriver?.name ?? 'Your Profile',
|
||||
requests: driverRequests,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 2. Leagues where the user is admin/owner
|
||||
const allLeagues = await leagueRepo.findAll();
|
||||
const allLeagues = await leagueService.getAllLeagues();
|
||||
for (const league of allLeagues) {
|
||||
const membership = await leagueMembershipRepo.getMembership(league.id, currentDriverId);
|
||||
if (membership && isLeagueAdminOrHigherRole(membership.role)) {
|
||||
const membership = await leagueMembershipService.getMembership(league.id, currentDriverId);
|
||||
if (membership && LeagueRoleUtility.isLeagueAdminOrHigherRole(membership.role)) {
|
||||
// Load sponsorship requests for this league's active season
|
||||
try {
|
||||
// For simplicity, we'll query by season entityType - in production you'd get the active season ID
|
||||
const leagueResult = await sponsorshipService.getPendingSponsorshipRequests({
|
||||
const leagueRequests = await sponsorshipService.getPendingSponsorshipRequests({
|
||||
entityType: 'season',
|
||||
entityId: league.id, // Using league ID as a proxy for now
|
||||
});
|
||||
|
||||
if (leagueResult && leagueResult.requests.length > 0) {
|
||||
|
||||
if (leagueRequests.length > 0) {
|
||||
allSections.push({
|
||||
entityType: 'season',
|
||||
entityId: league.id,
|
||||
entityName: league.name,
|
||||
requests: leagueResult.requests,
|
||||
requests: leagueRequests,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -84,28 +80,28 @@ export default function SponsorshipRequestsPage() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 3. Teams where the user is owner/manager
|
||||
const allTeams = await teamRepo.findAll();
|
||||
const allTeams = await teamService.getAllTeams();
|
||||
for (const team of allTeams) {
|
||||
const membership = await teamMembershipRepo.getMembership(team.id, currentDriverId);
|
||||
const membership = await teamService.getMembership(team.id, currentDriverId);
|
||||
if (membership && (membership.role === 'owner' || membership.role === 'manager')) {
|
||||
const teamResult = await sponsorshipService.getPendingSponsorshipRequests({
|
||||
const teamRequests = await sponsorshipService.getPendingSponsorshipRequests({
|
||||
entityType: 'team',
|
||||
entityId: team.id,
|
||||
});
|
||||
|
||||
if (teamResult && teamResult.requests.length > 0) {
|
||||
|
||||
if (teamRequests.length > 0) {
|
||||
allSections.push({
|
||||
entityType: 'team',
|
||||
entityId: team.id,
|
||||
entityName: team.name,
|
||||
requests: teamResult.requests,
|
||||
requests: teamRequests,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
setSections(allSections);
|
||||
} catch (err) {
|
||||
console.error('Failed to load sponsorship requests:', err);
|
||||
@@ -120,24 +116,14 @@ export default function SponsorshipRequestsPage() {
|
||||
}, [loadAllRequests]);
|
||||
|
||||
const handleAccept = async (requestId: string) => {
|
||||
const useCase = getAcceptSponsorshipRequestUseCase();
|
||||
await useCase.execute({
|
||||
requestId,
|
||||
respondedBy: currentDriverId,
|
||||
});
|
||||
const { sponsorshipService } = useServices();
|
||||
await sponsorshipService.acceptSponsorshipRequest(requestId, currentDriverId);
|
||||
await loadAllRequests();
|
||||
};
|
||||
|
||||
const handleReject = async (requestId: string, reason?: string) => {
|
||||
const useCase = getRejectSponsorshipRequestUseCase();
|
||||
const input: { requestId: string; respondedBy: string; reason?: string } = {
|
||||
requestId,
|
||||
respondedBy: currentDriverId,
|
||||
};
|
||||
if (typeof reason === 'string') {
|
||||
input.reason = reason;
|
||||
}
|
||||
await useCase.execute(input);
|
||||
const { sponsorshipService } = useServices();
|
||||
await sponsorshipService.rejectSponsorshipRequest(requestId, currentDriverId, reason);
|
||||
await loadAllRequests();
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user