| [40ac7a9] | 1 | import React, { useEffect, useState } from 'react';
|
|---|
| 2 | import {
|
|---|
| [1bf6e1f] | 3 | Container, Box, Typography, Button, Grid, IconButton, Dialog, DialogTitle, DialogContent
|
|---|
| [40ac7a9] | 4 | } from '@mui/material';
|
|---|
| 5 | import CloseIcon from '@mui/icons-material/Close';
|
|---|
| 6 | import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh';
|
|---|
| [1bf6e1f] | 7 | import ListIcon from '@mui/icons-material/List';
|
|---|
| [40ac7a9] | 8 |
|
|---|
| [1bf6e1f] | 9 | import BuildDetailsDialog from '../../components/BuildDetailsDialog';
|
|---|
| 10 | import BuildCard from '../../components/BuildCard';
|
|---|
| [40ac7a9] | 11 |
|
|---|
| [1bf6e1f] | 12 | import { onGetApprovedBuilds, onGetAuthState, onCloneBuild } from '../+Layout.telefunc';
|
|---|
| [40ac7a9] | 13 |
|
|---|
| 14 | export default function HomePage() {
|
|---|
| 15 | const [data, setData] = useState<any>(null);
|
|---|
| [e599341] | 16 | const [allRanked, setAllRanked] = useState<any[]>([]);
|
|---|
| [40ac7a9] | 17 |
|
|---|
| [1bf6e1f] | 18 | const [selectedBuildId, setSelectedBuildId] = useState<number | null>(null);
|
|---|
| [40ac7a9] | 19 | const [openRankedPopup, setOpenRankedPopup] = useState(false);
|
|---|
| 20 |
|
|---|
| 21 | useEffect(() => {
|
|---|
| [1bf6e1f] | 22 | async function loadSite() {
|
|---|
| 23 | try {
|
|---|
| [40ac7a9] | 24 | const [
|
|---|
| [e599341] | 25 | authData,
|
|---|
| 26 | highestRankedBuilds,
|
|---|
| 27 | communityBuilds,
|
|---|
| 28 | fullRankedList
|
|---|
| [40ac7a9] | 29 | ] = await Promise.all([
|
|---|
| [67dfe57] | 30 | onGetAuthState(),
|
|---|
| [e599341] | 31 | onGetApprovedBuilds({ limit: 5 , sort: 'rating_desc' }),
|
|---|
| 32 | onGetApprovedBuilds({ limit: 12 }),
|
|---|
| 33 | onGetApprovedBuilds({ limit: 10, sort: 'rating_desc' })
|
|---|
| [40ac7a9] | 34 | ]);
|
|---|
| 35 |
|
|---|
| 36 | setData({
|
|---|
| 37 | isLoggedIn: authData.isLoggedIn,
|
|---|
| 38 | userId: authData.userId,
|
|---|
| 39 | prebuilts: highestRankedBuilds,
|
|---|
| [1bf6e1f] | 40 | communityBuilds: communityBuilds
|
|---|
| [40ac7a9] | 41 | });
|
|---|
| [e599341] | 42 | setAllRanked(fullRankedList);
|
|---|
| [40ac7a9] | 43 | } catch (error) {
|
|---|
| 44 | console.error("Error loading homepage data:", error);
|
|---|
| 45 | }
|
|---|
| [1bf6e1f] | 46 | }
|
|---|
| 47 | loadSite();
|
|---|
| 48 | }, []);
|
|---|
| [40ac7a9] | 49 |
|
|---|
| [1bf6e1f] | 50 | const handleCloneWrapper = async (buildId: number) => {
|
|---|
| [40ac7a9] | 51 | if (!data?.isLoggedIn) return alert("Please login to clone builds!");
|
|---|
| [1bf6e1f] | 52 | if (confirm(`Clone this build to your dashboard?`)) {
|
|---|
| 53 | await onCloneBuild({ buildId });
|
|---|
| [40ac7a9] | 54 | alert("Build cloned! Check your dashboard.");
|
|---|
| [1bf6e1f] | 55 | setSelectedBuildId(null);
|
|---|
| [40ac7a9] | 56 | }
|
|---|
| 57 | };
|
|---|
| 58 |
|
|---|
| 59 | if (!data) return <Box sx={{ p: 10, textAlign: 'center' }}>Loading Forge...</Box>;
|
|---|
| 60 |
|
|---|
| 61 | return (
|
|---|
| 62 | <Box>
|
|---|
| 63 | <Box sx={{
|
|---|
| 64 | position: 'relative', height: '500px',
|
|---|
| 65 | display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'white',
|
|---|
| [1bf6e1f] | 66 | '&::before': {
|
|---|
| 67 | content: '""', position: 'absolute', top: 0, left: 0, width: '100%', height: '100%',
|
|---|
| 68 | backgroundColor: 'rgba(0,0,0,0.6)'
|
|---|
| 69 | }
|
|---|
| [40ac7a9] | 70 | }}>
|
|---|
| 71 | <Box sx={{ position: 'relative', textAlign: 'center', zIndex: 1, p: 2 }}>
|
|---|
| 72 | <Typography variant="h2" fontWeight="bold" gutterBottom>Forge Your Ultimate Machine</Typography>
|
|---|
| [1bf6e1f] | 73 | <Typography variant="h5" sx={{ maxWidth: '800px', mx: 'auto', mb: 1 }}>
|
|---|
| [40ac7a9] | 74 | Build, share, discuss, and discover custom PC configurations.
|
|---|
| 75 | </Typography>
|
|---|
| [e599341] | 76 | <Button variant="contained" size="large" href="/forge" startIcon={<AutoFixHighIcon />}
|
|---|
| [1bf6e1f] | 77 | sx={{ fontSize: '1.2rem', px: 4, py: 1.5, mt: 2 }}>
|
|---|
| [40ac7a9] | 78 | Start Forging
|
|---|
| 79 | </Button>
|
|---|
| 80 | </Box>
|
|---|
| 81 | </Box>
|
|---|
| 82 |
|
|---|
| 83 | <Container maxWidth="xl" sx={{ mt: 6, mb: 10 }}>
|
|---|
| [1bf6e1f] | 84 | <Box sx={{
|
|---|
| 85 | mb: 4, borderLeft: '5px solid #ff8201', pl: 2,
|
|---|
| 86 | display: 'flex', justifyContent: 'space-between', alignItems: 'end'
|
|---|
| 87 | }}>
|
|---|
| [40ac7a9] | 88 | <Box>
|
|---|
| 89 | <Typography variant="h4" fontWeight="bold">Hall of Fame</Typography>
|
|---|
| [1bf6e1f] | 90 | <Typography variant="subtitle1" color="text.secondary">
|
|---|
| 91 | The highest rated configurations from across the community.
|
|---|
| 92 | </Typography>
|
|---|
| [40ac7a9] | 93 | </Box>
|
|---|
| [1bf6e1f] | 94 | <Button variant="outlined" startIcon={<ListIcon />} onClick={() => setOpenRankedPopup(true)}>
|
|---|
| [40ac7a9] | 95 | Show All Top Ranked
|
|---|
| 96 | </Button>
|
|---|
| 97 | </Box>
|
|---|
| 98 |
|
|---|
| [e599341] | 99 | <Grid container spacing={3} sx={{ mb: 8, alignItems: 'stretch' }}>
|
|---|
| [1bf6e1f] | 100 | {data.prebuilts.slice(0, 3).map((build: any) => (
|
|---|
| 101 | <Grid item xs={12} sm={6} md={3} key={build.id} sx={{ display: 'flex' }}>
|
|---|
| 102 | <Box sx={{ width: '100%' }}>
|
|---|
| 103 | <BuildCard
|
|---|
| 104 | build={build}
|
|---|
| 105 | onClick={() => setSelectedBuildId(build.id)}
|
|---|
| 106 | />
|
|---|
| 107 | </Box>
|
|---|
| [40ac7a9] | 108 | </Grid>
|
|---|
| 109 | ))}
|
|---|
| 110 | </Grid>
|
|---|
| 111 |
|
|---|
| 112 | <SectionHeader title="Community Forge" subtitle="Fresh builds from users around the world."/>
|
|---|
| 113 | <Grid container spacing={3}>
|
|---|
| 114 | {data.communityBuilds.map((build: any) => (
|
|---|
| 115 | <Grid item xs={12} sm={6} md={3} key={build.id}>
|
|---|
| [1bf6e1f] | 116 | <BuildCard
|
|---|
| 117 | build={build}
|
|---|
| 118 | onClick={() => setSelectedBuildId(build.id)}
|
|---|
| 119 | />
|
|---|
| [40ac7a9] | 120 | </Grid>
|
|---|
| 121 | ))}
|
|---|
| 122 | </Grid>
|
|---|
| 123 |
|
|---|
| 124 | <Box sx={{ display: 'flex', justifyContent: 'center', mt: 6 }}>
|
|---|
| 125 | <Button variant="outlined" size="large" href="/completed-builds">View All Community Builds</Button>
|
|---|
| 126 | </Box>
|
|---|
| 127 | </Container>
|
|---|
| 128 |
|
|---|
| [1bf6e1f] | 129 | <BuildDetailsDialog
|
|---|
| 130 | open={!!selectedBuildId}
|
|---|
| 131 | buildId={selectedBuildId}
|
|---|
| 132 | currentUser={data.userId}
|
|---|
| 133 | onClose={() => setSelectedBuildId(null)}
|
|---|
| 134 | onClone={handleCloneWrapper}
|
|---|
| 135 | />
|
|---|
| [40ac7a9] | 136 |
|
|---|
| [e599341] | 137 | <Dialog open={openRankedPopup} onClose={() => setOpenRankedPopup(false)} maxWidth="lg" fullWidth scroll="paper">
|
|---|
| [40ac7a9] | 138 | <DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
|---|
| [e599341] | 139 | Hall of Fame (Top Rated)
|
|---|
| [40ac7a9] | 140 | <IconButton onClick={() => setOpenRankedPopup(false)}><CloseIcon /></IconButton>
|
|---|
| 141 | </DialogTitle>
|
|---|
| 142 | <DialogContent dividers>
|
|---|
| 143 | <Grid container spacing={3}>
|
|---|
| [e599341] | 144 | {allRanked.map((build: any, index: number) => (
|
|---|
| 145 | <Grid item xs={12} sm={6} md={3} key={`ranked-${build.id}`}>
|
|---|
| 146 | <Box sx={{ position: 'relative' }}>
|
|---|
| 147 | <Box sx={{
|
|---|
| 148 | position: 'absolute', top: -10, left: -10, zIndex: 1,
|
|---|
| 149 | width: 30, height: 30, borderRadius: '50%',
|
|---|
| 150 | bgcolor: index < 3 ? '#ff8201' : 'grey.700', color: 'white',
|
|---|
| 151 | display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 'bold'
|
|---|
| 152 | }}>
|
|---|
| 153 | #{index + 1}
|
|---|
| 154 | </Box>
|
|---|
| 155 | <BuildCard
|
|---|
| 156 | build={build}
|
|---|
| 157 | onClick={() => {
|
|---|
| 158 | setOpenRankedPopup(false);
|
|---|
| 159 | setSelectedBuildId(build.id);
|
|---|
| 160 | }}
|
|---|
| 161 | />
|
|---|
| 162 | </Box>
|
|---|
| [40ac7a9] | 163 | </Grid>
|
|---|
| 164 | ))}
|
|---|
| 165 | </Grid>
|
|---|
| 166 | </DialogContent>
|
|---|
| 167 | </Dialog>
|
|---|
| 168 |
|
|---|
| 169 | </Box>
|
|---|
| 170 | );
|
|---|
| 171 | }
|
|---|
| 172 |
|
|---|
| 173 | function SectionHeader({ title, subtitle }: { title: string, subtitle: string }) {
|
|---|
| 174 | return (
|
|---|
| [e599341] | 175 | <Box sx={{ mb: 4, borderLeft: '5px solid #ff8201', pl: 2 }}>
|
|---|
| [40ac7a9] | 176 | <Typography variant="h4" fontWeight="bold" color="text.primary">{title}</Typography>
|
|---|
| 177 | <Typography variant="subtitle1" color="text.secondary">{subtitle}</Typography>
|
|---|
| 178 | </Box>
|
|---|
| 179 | );
|
|---|
| 180 | }
|
|---|