source: pages/dashboard/user/+Page.tsx@ b6e1b3c

main
Last change on this file since b6e1b3c was b6e1b3c, checked in by Mihail <mihail2.naumov@…>, 6 months ago

Added delete button and still fixing naming problem with build creation

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