website refactor
This commit is contained in:
@@ -1,54 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { ProfileTemplate } from '@/templates/ProfileTemplate';
|
||||
import { type ProfileTab } from '@/components/profile/ProfileNavTabs';
|
||||
import type { ProfileViewData } from '@/lib/view-data/ProfileViewData';
|
||||
|
||||
interface ProfilePageClientProps {
|
||||
viewData: ProfileViewData;
|
||||
mode: 'profile-exists' | 'needs-profile';
|
||||
}
|
||||
|
||||
export function ProfilePageClient({ viewData, mode }: ProfilePageClientProps) {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const tabParam = searchParams.get('tab') as ProfileTab | null;
|
||||
|
||||
const [activeTab, setActiveTab] = useState<ProfileTab>(tabParam || 'overview');
|
||||
const [friendRequestSent, setFriendRequestSent] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
if (activeTab === 'overview') {
|
||||
params.delete('tab');
|
||||
} else {
|
||||
params.set('tab', activeTab);
|
||||
}
|
||||
const query = params.toString();
|
||||
const currentQuery = searchParams.toString();
|
||||
|
||||
if (query !== currentQuery) {
|
||||
router.replace(`/profile${query ? `?${query}` : ''}`, { scroll: false });
|
||||
}
|
||||
}, [activeTab, searchParams, router]);
|
||||
|
||||
useEffect(() => {
|
||||
const tab = searchParams.get('tab') as ProfileTab | null;
|
||||
if (tab && tab !== activeTab) {
|
||||
setActiveTab(tab);
|
||||
}
|
||||
}, [searchParams, activeTab]);
|
||||
|
||||
return (
|
||||
<ProfileTemplate
|
||||
viewData={viewData}
|
||||
mode={mode}
|
||||
activeTab={activeTab}
|
||||
onTabChange={setActiveTab}
|
||||
friendRequestSent={friendRequestSent}
|
||||
onFriendRequestSend={() => setFriendRequestSent(true)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { UploadDropzone } from '@/components/media/UploadDropzone';
|
||||
import { routes } from '@/lib/routing/RouteConfig';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Container } from '@/ui/Container';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { MediaMetaPanel, mapMediaMetadata } from '@/ui/MediaMetaPanel';
|
||||
import { MediaPreviewCard } from '@/ui/MediaPreviewCard';
|
||||
import { Text } from '@/ui/Text';
|
||||
import Link from 'next/link';
|
||||
import { useState } from 'react';
|
||||
|
||||
export function ProfileLiveryUploadPageClient() {
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
|
||||
const [isUploading, setIsUploading] = useState(false);
|
||||
|
||||
const handleFilesSelected = (files: File[]) => {
|
||||
if (files.length > 0) {
|
||||
const file = files[0];
|
||||
setSelectedFile(file);
|
||||
const url = URL.createObjectURL(file);
|
||||
setPreviewUrl(url);
|
||||
} else {
|
||||
setSelectedFile(null);
|
||||
setPreviewUrl(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpload = async () => {
|
||||
if (!selectedFile) return;
|
||||
setIsUploading(true);
|
||||
// Mock upload delay
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
setIsUploading(false);
|
||||
alert('Livery uploaded successfully! (Mock)');
|
||||
};
|
||||
|
||||
return (
|
||||
<Container size="md">
|
||||
<Box mb={6}>
|
||||
<Heading level={1}>Upload livery</Heading>
|
||||
<Text color="text-gray-500">
|
||||
Upload your custom car livery. Supported formats: .png, .jpg, .tga
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Box display="grid" responsiveGridCols={{ base: 1, md: 2 }} gap={6}>
|
||||
<Box>
|
||||
<Card>
|
||||
<UploadDropzone
|
||||
onFilesSelected={handleFilesSelected}
|
||||
accept=".png,.jpg,.jpeg,.tga"
|
||||
maxSize={10 * 1024 * 1024} // 10MB
|
||||
isLoading={isUploading}
|
||||
/>
|
||||
|
||||
<Box mt={6} display="flex" justifyContent="end" gap={3}>
|
||||
<Link href={routes.protected.profileLiveries}>
|
||||
<Button variant="ghost">Cancel</Button>
|
||||
</Link>
|
||||
<Button
|
||||
variant="primary"
|
||||
disabled={!selectedFile || isUploading}
|
||||
onClick={handleUpload}
|
||||
isLoading={isUploading}
|
||||
>
|
||||
Upload Livery
|
||||
</Button>
|
||||
</Box>
|
||||
</Card>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
{previewUrl ? (
|
||||
<Box display="flex" flexDirection="col" gap={6}>
|
||||
<MediaPreviewCard
|
||||
src={previewUrl}
|
||||
title={selectedFile?.name}
|
||||
subtitle="Preview"
|
||||
aspectRatio="16/9"
|
||||
/>
|
||||
|
||||
<MediaMetaPanel
|
||||
items={mapMediaMetadata({
|
||||
filename: selectedFile?.name,
|
||||
size: selectedFile?.size,
|
||||
contentType: selectedFile?.type || 'image/tga',
|
||||
createdAt: new Date(),
|
||||
})}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<Card center p={12}>
|
||||
<Text color="text-gray-500" align="center">
|
||||
Select a file to see preview and details
|
||||
</Text>
|
||||
</Card>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { ProfileLiveryUploadPageClient } from './ProfileLiveryUploadPageClient';
|
||||
import { ProfileLiveryUploadPageClient } from '@/client-wrapper/ProfileLiveryUploadPageClient';
|
||||
|
||||
export default async function ProfileLiveryUploadPage() {
|
||||
return <ProfileLiveryUploadPageClient />;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ProfilePageQuery } from '@/lib/page-queries/ProfilePageQuery';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { ProfilePageClient } from './ProfilePageClient';
|
||||
import { ProfilePageClient } from '@/client-wrapper/ProfilePageClient';
|
||||
|
||||
export default async function ProfilePage() {
|
||||
const query = new ProfilePageQuery();
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { InlineNotice } from '@/ui/InlineNotice';
|
||||
import { ProgressLine } from '@/ui/ProgressLine';
|
||||
import type { Result } from '@/lib/contracts/Result';
|
||||
import type { ProfileViewData } from '@/lib/view-data/ProfileViewData';
|
||||
import { ProfileSettingsTemplate } from '@/templates/ProfileSettingsTemplate';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
|
||||
interface ProfileSettingsPageClientProps {
|
||||
viewData: ProfileViewData;
|
||||
onSave: (updates: { bio?: string; country?: string }) => Promise<Result<void, string>>;
|
||||
}
|
||||
|
||||
export function ProfileSettingsPageClient({ viewData, onSave }: ProfileSettingsPageClientProps) {
|
||||
const router = useRouter();
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const [bio, setBio] = useState(viewData.driver.bio || '');
|
||||
const [country, setCountry] = useState(viewData.driver.countryCode);
|
||||
|
||||
const handleSave = async () => {
|
||||
setIsSaving(true);
|
||||
setError(null);
|
||||
try {
|
||||
const result = await onSave({ bio, country });
|
||||
if (result.isErr()) {
|
||||
setError(result.getError());
|
||||
} else {
|
||||
router.refresh();
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to save settings');
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProgressLine isLoading={isSaving} />
|
||||
{error && (
|
||||
<Box position="fixed" top={4} right={4} zIndex={50} maxWidth="md">
|
||||
<InlineNotice
|
||||
variant="error"
|
||||
title="Update Failed"
|
||||
message={error}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
<ProfileSettingsTemplate
|
||||
viewData={viewData}
|
||||
bio={bio}
|
||||
country={country}
|
||||
onBioChange={setBio}
|
||||
onCountryChange={setCountry}
|
||||
onSave={handleSave}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ProfilePageQuery } from '@/lib/page-queries/ProfilePageQuery';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { updateProfileAction } from '@/app/actions/profileActions';
|
||||
import { ProfileSettingsPageClient } from './ProfileSettingsPageClient';
|
||||
import { ProfileSettingsPageClient } from '@/client-wrapper/ProfileSettingsPageClient';
|
||||
|
||||
export default async function ProfileSettingsPage() {
|
||||
const query = new ProfilePageQuery();
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { InlineNotice } from '@/ui/InlineNotice';
|
||||
import { ProgressLine } from '@/ui/ProgressLine';
|
||||
import type { Result } from '@/lib/contracts/Result';
|
||||
import type { SponsorshipRequestsViewData } from '@/lib/view-data/SponsorshipRequestsViewData';
|
||||
import { SponsorshipRequestsTemplate } from '@/templates/SponsorshipRequestsTemplate';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
|
||||
interface SponsorshipRequestsClientProps {
|
||||
viewData: SponsorshipRequestsViewData;
|
||||
onAccept: (requestId: string) => Promise<Result<void, string>>;
|
||||
onReject: (requestId: string, reason?: string) => Promise<Result<void, string>>;
|
||||
}
|
||||
|
||||
export function SponsorshipRequestsClient({ viewData, onAccept, onReject }: SponsorshipRequestsClientProps) {
|
||||
const router = useRouter();
|
||||
const [isProcessing, setIsProcessing] = useState<string | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const handleAccept = async (requestId: string) => {
|
||||
setIsProcessing(requestId);
|
||||
setError(null);
|
||||
const result = await onAccept(requestId);
|
||||
if (result.isErr()) {
|
||||
setError(result.getError());
|
||||
setIsProcessing(null);
|
||||
} else {
|
||||
router.refresh();
|
||||
setIsProcessing(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleReject = async (requestId: string, reason?: string) => {
|
||||
setIsProcessing(requestId);
|
||||
setError(null);
|
||||
const result = await onReject(requestId, reason);
|
||||
if (result.isErr()) {
|
||||
setError(result.getError());
|
||||
setIsProcessing(null);
|
||||
} else {
|
||||
router.refresh();
|
||||
setIsProcessing(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProgressLine isLoading={!!isProcessing} />
|
||||
{error && (
|
||||
<Box position="fixed" top={4} right={4} zIndex={50} maxWidth="md">
|
||||
<InlineNotice
|
||||
variant="error"
|
||||
title="Action Failed"
|
||||
message={error}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
<SponsorshipRequestsTemplate
|
||||
viewData={viewData}
|
||||
onAccept={handleAccept}
|
||||
onReject={handleReject}
|
||||
processingId={isProcessing}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import type { Result } from '@/lib/contracts/Result';
|
||||
import type { SponsorshipRequestsViewData } from '@/lib/view-data/SponsorshipRequestsViewData';
|
||||
import { SponsorshipRequestsTemplate } from '@/templates/SponsorshipRequestsTemplate';
|
||||
|
||||
interface SponsorshipRequestsPageClientProps {
|
||||
viewData: SponsorshipRequestsViewData;
|
||||
onAccept: (requestId: string) => Promise<Result<void, string>>;
|
||||
onReject: (requestId: string, reason?: string) => Promise<Result<void, string>>;
|
||||
}
|
||||
|
||||
export function SponsorshipRequestsPageClient({ viewData, onAccept, onReject }: SponsorshipRequestsPageClientProps) {
|
||||
return (
|
||||
<SponsorshipRequestsTemplate
|
||||
viewData={viewData}
|
||||
onAccept={async (requestId) => {
|
||||
await onAccept(requestId);
|
||||
}}
|
||||
onReject={async (requestId, reason) => {
|
||||
await onReject(requestId, reason);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { notFound } from 'next/navigation';
|
||||
import { SponsorshipRequestsPageQuery } from '@/lib/page-queries/SponsorshipRequestsPageQuery';
|
||||
import { SponsorshipRequestsClient } from './SponsorshipRequestsClient';
|
||||
import { SponsorshipRequestsClient } from '@/client-wrapper/SponsorshipRequestsClient';
|
||||
import { acceptSponsorshipRequest, rejectSponsorshipRequest } from '@/app/actions/sponsorshipActions';
|
||||
|
||||
export default async function SponsorshipRequestsPage() {
|
||||
|
||||
Reference in New Issue
Block a user