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