source: pages/dashboard/user/+Page.tsx@ 4ceb725

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

Fixed small margin errors

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