| [40ac7a9] | 1 | import React, {useEffect, useState} from 'react';
|
|---|
| 2 | import {
|
|---|
| 3 | Container,
|
|---|
| 4 | Paper,
|
|---|
| 5 | Typography,
|
|---|
| 6 | Box,
|
|---|
| 7 | Avatar,
|
|---|
| 8 | Divider,
|
|---|
| 9 | Button,
|
|---|
| 10 | CircularProgress,
|
|---|
| [e599341] | 11 | IconButton,
|
|---|
| 12 | Grid,
|
|---|
| 13 | Dialog,
|
|---|
| 14 | DialogTitle,
|
|---|
| 15 | DialogContent,
|
|---|
| 16 | DialogActions
|
|---|
| [40ac7a9] | 17 | } from '@mui/material';
|
|---|
| 18 | import CloseIcon from '@mui/icons-material/Close';
|
|---|
| 19 |
|
|---|
| 20 | import PersonIcon from '@mui/icons-material/Person';
|
|---|
| 21 | import ComputerIcon from '@mui/icons-material/Computer';
|
|---|
| 22 | import FavoriteIcon from '@mui/icons-material/Favorite';
|
|---|
| 23 |
|
|---|
| 24 | import {getUserInfoAndData, onDeleteBuild} from "./userDashboard.telefunc";
|
|---|
| [e599341] | 25 | import {onCloneBuild} from "../../+Layout.telefunc";
|
|---|
| 26 |
|
|---|
| 27 | import BuildCard from "../../../components/BuildCard";
|
|---|
| 28 | import BuildDetailsDialog from "../../../components/BuildDetailsDialog";
|
|---|
| [40ac7a9] | 29 |
|
|---|
| 30 | type DashboardData = {
|
|---|
| 31 | user: { username: string; email?: string; [key: string]: any };
|
|---|
| 32 | userBuilds: any[];
|
|---|
| 33 | favoriteBuilds: any[];
|
|---|
| 34 | };
|
|---|
| 35 |
|
|---|
| 36 | export default function UserDashboard() {
|
|---|
| 37 | const [data, setData] = useState<DashboardData | null>(null);
|
|---|
| 38 | const [loading, setLoading] = useState(true);
|
|---|
| 39 | const [error, setError] = useState<string | null>(null);
|
|---|
| [e599341] | 40 |
|
|---|
| [40ac7a9] | 41 | const [openMyBuildsDialog, setOpenMyBuildsDialog] = useState(false);
|
|---|
| [e599341] | 42 | const [openFavoritesDialog, setOpenFavoritesDialog] = useState(false);
|
|---|
| 43 |
|
|---|
| 44 | const [selectedBuildId, setSelectedBuildId] = useState<number | null>(null);
|
|---|
| [40ac7a9] | 45 |
|
|---|
| 46 | const loadData = () => {
|
|---|
| 47 | getUserInfoAndData()
|
|---|
| 48 | .then((result) => {
|
|---|
| 49 | setData(result);
|
|---|
| 50 | setLoading(false);
|
|---|
| 51 | })
|
|---|
| 52 | .catch((err) => {
|
|---|
| 53 | console.error(err);
|
|---|
| 54 | setError("Failed to load dashboard. Please log in.");
|
|---|
| 55 | setLoading(false);
|
|---|
| 56 | });
|
|---|
| 57 | };
|
|---|
| 58 |
|
|---|
| 59 | useEffect(() => {
|
|---|
| 60 | loadData();
|
|---|
| 61 | }, []);
|
|---|
| 62 |
|
|---|
| 63 | const handleDelete = async (buildId: number) => {
|
|---|
| 64 | if (!confirm("Are you sure you want to delete this build?")) return;
|
|---|
| 65 | try {
|
|---|
| 66 | await onDeleteBuild({buildId});
|
|---|
| 67 | loadData();
|
|---|
| [e599341] | 68 | setSelectedBuildId(null);
|
|---|
| [40ac7a9] | 69 | } catch (e) {
|
|---|
| 70 | alert("Failed to delete build");
|
|---|
| 71 | }
|
|---|
| 72 | };
|
|---|
| 73 |
|
|---|
| [e599341] | 74 | const handleCloneWrapper = async (buildId: number) => {
|
|---|
| 75 | if (!data?.user) return alert("Please login to clone builds!");
|
|---|
| 76 | if (confirm(`Clone this build to your dashboard?`)) {
|
|---|
| 77 | await onCloneBuild({buildId});
|
|---|
| 78 | alert("Build cloned! Check your dashboard.");
|
|---|
| 79 | setSelectedBuildId(null);
|
|---|
| 80 | loadData();
|
|---|
| 81 | }
|
|---|
| 82 | };
|
|---|
| 83 |
|
|---|
| [40ac7a9] | 84 | if (loading) return <Box sx={{display: 'flex', justifyContent: 'center', mt: 10}}><CircularProgress/></Box>;
|
|---|
| 85 | if (error || !data) return <Container sx={{mt: 5, textAlign: 'center'}}><Typography color="error"
|
|---|
| 86 | variant="h6">{error}</Typography><Button
|
|---|
| 87 | href="/auth/login" variant="contained">Login</Button></Container>;
|
|---|
| [e599341] | 88 | // console.log("Current User ID passed to dialog:", data?.user?.id);
|
|---|
| [40ac7a9] | 89 |
|
|---|
| 90 | return (
|
|---|
| 91 | <Container maxWidth="xl" sx={{mt: 4, mb: 4, color: 'white'}}>
|
|---|
| 92 | <Grid container spacing={3}>
|
|---|
| 93 | <Grid item xs={12} md={3}>
|
|---|
| 94 | <Paper elevation={3}
|
|---|
| 95 | sx={{p: 3, display: 'flex', flexDirection: 'column', alignItems: 'center', height: '100%'}}>
|
|---|
| 96 | <Avatar sx={{width: 100, height: 100, mb: 2, bgcolor: 'primary.main'}}><PersonIcon
|
|---|
| 97 | sx={{fontSize: 60}}/></Avatar>
|
|---|
| 98 | <Typography variant="h5" fontWeight="bold" gutterBottom>{data.user.username}</Typography>
|
|---|
| 99 | <Typography variant="body2" color="text.secondary" gutterBottom>{data.user.email}</Typography>
|
|---|
| 100 | <Divider sx={{width: '100%', my: 2}}/>
|
|---|
| 101 | </Paper>
|
|---|
| 102 | </Grid>
|
|---|
| 103 |
|
|---|
| 104 | <Grid item xs={12} md={9}>
|
|---|
| 105 | <Grid container spacing={3} direction="column">
|
|---|
| 106 |
|
|---|
| 107 | <Grid item xs={12}>
|
|---|
| 108 | <Paper elevation={3} sx={{p: 3, minHeight: '300px', bgcolor: '#1e1e1e'}}>
|
|---|
| [e599341] | 109 | <Box sx={{
|
|---|
| 110 | display: 'flex',
|
|---|
| 111 | alignItems: 'center',
|
|---|
| 112 | justifyContent: 'space-between',
|
|---|
| 113 | mb: 2
|
|---|
| 114 | }}>
|
|---|
| 115 | <Box sx={{display: 'flex', alignItems: 'center'}}>
|
|---|
| 116 | <FavoriteIcon color="error" sx={{mr: 1}}/>
|
|---|
| 117 | <Typography variant="h6" fontWeight="bold">Favorited Builds</Typography>
|
|---|
| 118 | </Box>
|
|---|
| 119 |
|
|---|
| 120 | {data.favoriteBuilds.length > 4 && (
|
|---|
| 121 | <Button
|
|---|
| 122 | variant="outlined"
|
|---|
| 123 | size="small"
|
|---|
| 124 | onClick={() => setOpenFavoritesDialog(true)}
|
|---|
| 125 | >
|
|---|
| 126 | See All ({data.favoriteBuilds.length})
|
|---|
| 127 | </Button>
|
|---|
| 128 | )}
|
|---|
| [40ac7a9] | 129 | </Box>
|
|---|
| 130 | <Divider sx={{mb: 2}}/>
|
|---|
| 131 |
|
|---|
| 132 | {data.favoriteBuilds.length === 0 ? (
|
|---|
| 133 | <Typography color="text.secondary" align="center" sx={{mt: 4}}>You haven't favorited
|
|---|
| 134 | any builds yet.</Typography>
|
|---|
| 135 | ) : (
|
|---|
| 136 | <Grid container spacing={2}>
|
|---|
| [e599341] | 137 | {data.favoriteBuilds.slice(0, 4).map((build: any) => (
|
|---|
| [40ac7a9] | 138 | <Grid item xs={12} sm={6} md={4} key={build.id}>
|
|---|
| [e599341] | 139 | <BuildCard
|
|---|
| 140 | build={build}
|
|---|
| 141 | onClick={() => setSelectedBuildId(build.id)}
|
|---|
| 142 | />
|
|---|
| [40ac7a9] | 143 | </Grid>
|
|---|
| 144 | ))}
|
|---|
| 145 | </Grid>
|
|---|
| 146 | )}
|
|---|
| 147 | </Paper>
|
|---|
| 148 | </Grid>
|
|---|
| 149 |
|
|---|
| 150 | <Grid item xs={12}>
|
|---|
| 151 | <Paper elevation={3} sx={{p: 3, minHeight: '300px', bgcolor: '#1e1e1e'}}>
|
|---|
| 152 |
|
|---|
| 153 | <Box sx={{
|
|---|
| 154 | display: 'flex',
|
|---|
| 155 | alignItems: 'center',
|
|---|
| 156 | justifyContent: 'space-between',
|
|---|
| 157 | mb: 2
|
|---|
| 158 | }}>
|
|---|
| 159 | <Box sx={{display: 'flex', alignItems: 'center'}}>
|
|---|
| 160 | <ComputerIcon color="primary" sx={{mr: 1}}/>
|
|---|
| 161 | <Typography variant="h6" fontWeight="bold">My Builds</Typography>
|
|---|
| 162 | </Box>
|
|---|
| 163 |
|
|---|
| 164 | {data.userBuilds.length > 4 && (
|
|---|
| 165 | <Button
|
|---|
| 166 | variant="outlined"
|
|---|
| 167 | size="small"
|
|---|
| 168 | onClick={() => setOpenMyBuildsDialog(true)}
|
|---|
| 169 | >
|
|---|
| 170 | See All ({data.userBuilds.length})
|
|---|
| 171 | </Button>
|
|---|
| 172 | )}
|
|---|
| 173 | </Box>
|
|---|
| 174 | <Divider sx={{mb: 2}}/>
|
|---|
| 175 |
|
|---|
| 176 | {data.userBuilds.length === 0 ? (
|
|---|
| 177 | <Box sx={{textAlign: 'center', mt: 4}}>
|
|---|
| 178 | <Typography color="text.secondary" gutterBottom>You haven't created any builds
|
|---|
| 179 | yet.</Typography>
|
|---|
| 180 | <Button variant="contained" href="/forge"
|
|---|
| 181 | sx={{color: 'white', fontWeight: 'bolder'}}>Start Forging</Button>
|
|---|
| 182 | </Box>
|
|---|
| 183 | ) : (
|
|---|
| 184 | <Grid container spacing={2}>
|
|---|
| [e599341] | 185 | {data.userBuilds.slice(0, 4).map((build: any) => (
|
|---|
| 186 | <Grid item xs={12} sm={6} md={4} key={build.id}>
|
|---|
| [40ac7a9] | 187 | <BuildCard
|
|---|
| 188 | build={build}
|
|---|
| [e599341] | 189 | onClick={() => setSelectedBuildId(build.id)}
|
|---|
| [40ac7a9] | 190 | />
|
|---|
| 191 | </Grid>
|
|---|
| 192 | ))}
|
|---|
| 193 | </Grid>
|
|---|
| 194 | )}
|
|---|
| 195 | </Paper>
|
|---|
| 196 | </Grid>
|
|---|
| 197 |
|
|---|
| [e599341] | 198 | <Dialog open={openMyBuildsDialog} onClose={() => setOpenMyBuildsDialog(false)} maxWidth="lg"
|
|---|
| [40ac7a9] | 199 | fullWidth>
|
|---|
| [e599341] | 200 | <DialogTitle sx={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
|
|---|
| 201 | All My Builds
|
|---|
| [40ac7a9] | 202 | <IconButton onClick={() => setOpenMyBuildsDialog(false)}>
|
|---|
| 203 | <CloseIcon/>
|
|---|
| 204 | </IconButton>
|
|---|
| 205 | </DialogTitle>
|
|---|
| 206 | <DialogContent dividers>
|
|---|
| 207 | <Grid container spacing={2} sx={{mt: 1}}>
|
|---|
| 208 | {data.userBuilds.map((build: any) => (
|
|---|
| 209 | <Grid item xs={12} sm={6} md={4} key={build.id}>
|
|---|
| 210 | <BuildCard
|
|---|
| 211 | build={build}
|
|---|
| [e599341] | 212 | onClick={() => {
|
|---|
| 213 | setOpenMyBuildsDialog(false);
|
|---|
| 214 | setSelectedBuildId(build.id);
|
|---|
| 215 | }}
|
|---|
| [40ac7a9] | 216 | />
|
|---|
| 217 | </Grid>
|
|---|
| 218 | ))}
|
|---|
| 219 | </Grid>
|
|---|
| 220 | </DialogContent>
|
|---|
| 221 | <DialogActions>
|
|---|
| 222 | <Button onClick={() => setOpenMyBuildsDialog(false)}>Close</Button>
|
|---|
| 223 | </DialogActions>
|
|---|
| 224 | </Dialog>
|
|---|
| 225 |
|
|---|
| [e599341] | 226 | <Dialog open={openFavoritesDialog} onClose={() => setOpenFavoritesDialog(false)} maxWidth="lg"
|
|---|
| 227 | fullWidth>
|
|---|
| 228 | <DialogTitle sx={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
|
|---|
| 229 | All Favorited Builds
|
|---|
| 230 | <IconButton onClick={() => setOpenFavoritesDialog(false)}>
|
|---|
| 231 | <CloseIcon/>
|
|---|
| 232 | </IconButton>
|
|---|
| 233 | </DialogTitle>
|
|---|
| 234 | <DialogContent dividers>
|
|---|
| 235 | <Grid container spacing={2} sx={{mt: 1}}>
|
|---|
| 236 | {data.favoriteBuilds.map((build: any) => (
|
|---|
| 237 | <Grid item xs={12} sm={6} md={4} key={build.id}>
|
|---|
| 238 | <BuildCard
|
|---|
| 239 | build={build}
|
|---|
| 240 | onClick={() => {
|
|---|
| 241 | setOpenFavoritesDialog(false);
|
|---|
| 242 | setSelectedBuildId(build.id);
|
|---|
| 243 | }}
|
|---|
| 244 | />
|
|---|
| 245 | </Grid>
|
|---|
| 246 | ))}
|
|---|
| 247 | </Grid>
|
|---|
| 248 | </DialogContent>
|
|---|
| 249 | <DialogActions>
|
|---|
| 250 | <Button onClick={() => setOpenFavoritesDialog(false)}>Close</Button>
|
|---|
| 251 | </DialogActions>
|
|---|
| 252 | </Dialog>
|
|---|
| 253 |
|
|---|
| [40ac7a9] | 254 | </Grid>
|
|---|
| 255 | </Grid>
|
|---|
| 256 | </Grid>
|
|---|
| [e599341] | 257 |
|
|---|
| 258 | <BuildDetailsDialog
|
|---|
| 259 | open={!!selectedBuildId}
|
|---|
| 260 | buildId={selectedBuildId}
|
|---|
| 261 | currentUser={data?.user?.id ? Number(data.user.id) : undefined}
|
|---|
| 262 | onClose={() => {
|
|---|
| 263 | setSelectedBuildId(null);
|
|---|
| 264 | loadData();
|
|---|
| 265 | }}
|
|---|
| 266 | onClone={handleCloneWrapper}
|
|---|
| 267 | />
|
|---|
| [40ac7a9] | 268 | </Container>
|
|---|
| 269 | );
|
|---|
| 270 | }
|
|---|