source: pages/dashboard/user/+Page.tsx@ 3fca46c

main
Last change on this file since 3fca46c was 40ac7a9, checked in by Mihail <mihail2.naumov@…>, 7 months ago

Added frontend elements

  • Property mode set to 100644
File size: 10.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 Card,
12 CardContent,
13 CardActionArea,
14 IconButton
15} from '@mui/material';
16import Grid from '@mui/material/Grid';
17import {Dialog, DialogTitle, DialogContent, DialogActions} from '@mui/material';
18import CloseIcon from '@mui/icons-material/Close';
19
20import PersonIcon from '@mui/icons-material/Person';
21import ComputerIcon from '@mui/icons-material/Computer';
22import FavoriteIcon from '@mui/icons-material/Favorite';
23import DeleteIcon from '@mui/icons-material/Delete';
24import EditIcon from '@mui/icons-material/Edit';
25
26import {getUserInfoAndData, onDeleteBuild} from "./userDashboard.telefunc";
27
28type DashboardData = {
29 user: { username: string; email?: string; [key: string]: any };
30 userBuilds: any[];
31 favoriteBuilds: any[];
32};
33
34export default function UserDashboard() {
35 const [data, setData] = useState<DashboardData | null>(null);
36 const [loading, setLoading] = useState(true);
37 const [error, setError] = useState<string | null>(null);
38 const [openMyBuildsDialog, setOpenMyBuildsDialog] = useState(false);
39
40 const loadData = () => {
41 getUserInfoAndData()
42 .then((result) => {
43 setData(result);
44 setLoading(false);
45 })
46 .catch((err) => {
47 console.error(err);
48 setError("Failed to load dashboard. Please log in.");
49 setLoading(false);
50 });
51 };
52
53 useEffect(() => {
54 loadData();
55 }, []);
56
57 const handleDelete = async (buildId: number) => {
58 if (!confirm("Are you sure you want to delete this build?")) return;
59 try {
60 await onDeleteBuild({buildId});
61 loadData();
62 } catch (e) {
63 alert("Failed to delete build");
64 }
65 };
66
67 if (loading) return <Box sx={{display: 'flex', justifyContent: 'center', mt: 10}}><CircularProgress/></Box>;
68 if (error || !data) return <Container sx={{mt: 5, textAlign: 'center'}}><Typography color="error"
69 variant="h6">{error}</Typography><Button
70 href="/auth/login" variant="contained">Login</Button></Container>;
71
72
73 // @ts-ignore
74 return (
75 <Container maxWidth="xl" sx={{mt: 4, mb: 4, color: 'white'}}>
76 <Grid container spacing={3}>
77
78 <Grid item xs={12} md={3}>
79 <Paper elevation={3}
80 sx={{p: 3, display: 'flex', flexDirection: 'column', alignItems: 'center', height: '100%'}}>
81 <Avatar sx={{width: 100, height: 100, mb: 2, bgcolor: 'primary.main'}}><PersonIcon
82 sx={{fontSize: 60}}/></Avatar>
83 <Typography variant="h5" fontWeight="bold" gutterBottom>{data.user.username}</Typography>
84 <Typography variant="body2" color="text.secondary" gutterBottom>{data.user.email}</Typography>
85 <Divider sx={{width: '100%', my: 2}}/>
86 {/*<Button variant="outlined" fullWidth color="primary" sx={{mb: 1}}>Edit Profile</Button>*/}
87 </Paper>
88 </Grid>
89
90 <Grid item xs={12} md={9}>
91 <Grid container spacing={3} direction="column">
92
93 <Grid item xs={12}>
94 <Paper elevation={3} sx={{p: 3, minHeight: '300px', bgcolor: '#1e1e1e'}}>
95 <Box sx={{display: 'flex', alignItems: 'center', mb: 2}}>
96 <FavoriteIcon color="error" sx={{mr: 1}}/>
97 <Typography variant="h6" fontWeight="bold">Favorited Builds</Typography>
98 </Box>
99 <Divider sx={{mb: 2}}/>
100
101 {data.favoriteBuilds.length === 0 ? (
102 <Typography color="text.secondary" align="center" sx={{mt: 4}}>You haven't favorited
103 any builds yet.</Typography>
104 ) : (
105 <Grid container spacing={2}>
106 {data.favoriteBuilds.map((build: any) => (
107 <Grid item xs={12} sm={6} md={4} key={build.id}>
108 <BuildCard build={build}/>
109 </Grid>
110 ))}
111 </Grid>
112 )}
113 </Paper>
114 </Grid>
115
116 <Grid item xs={12}>
117 <Paper elevation={3} sx={{p: 3, minHeight: '300px', bgcolor: '#1e1e1e'}}>
118
119 <Box sx={{
120 display: 'flex',
121 alignItems: 'center',
122 justifyContent: 'space-between',
123 mb: 2
124 }}>
125 <Box sx={{display: 'flex', alignItems: 'center'}}>
126 <ComputerIcon color="primary" sx={{mr: 1}}/>
127 <Typography variant="h6" fontWeight="bold">My Builds</Typography>
128 </Box>
129
130 {data.userBuilds.length > 4 && (
131 <Button
132 variant="outlined"
133 size="small"
134 onClick={() => setOpenMyBuildsDialog(true)}
135 >
136 See All ({data.userBuilds.length})
137 </Button>
138 )}
139 </Box>
140 <Divider sx={{mb: 2}}/>
141
142 {data.userBuilds.length === 0 ? (
143 <Box sx={{textAlign: 'center', mt: 4}}>
144 <Typography color="text.secondary" gutterBottom>You haven't created any builds
145 yet.</Typography>
146 <Button variant="contained" href="/forge"
147 sx={{color: 'white', fontWeight: 'bolder'}}>Start Forging</Button>
148 </Box>
149 ) : (
150 <Grid container spacing={2}>
151 {data.userBuilds.slice(0, 3).map((build: any) => (
152 <Grid item xs={12} sm={6} md={3} key={build.id}>
153 <BuildCard
154 build={build}
155 onDelete={() => handleDelete(build.id)}
156 isOwner={true}
157 />
158 </Grid>
159 ))}
160 </Grid>
161 )}
162 </Paper>
163 </Grid>
164
165 <Dialog open={openMyBuildsDialog} onClose={() => setOpenMyBuildsDialog(false)} maxWidth="md"
166 fullWidth>
167 <DialogTitle sx={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>All
168 My Builds
169 <IconButton onClick={() => setOpenMyBuildsDialog(false)}>
170 <CloseIcon/>
171 </IconButton>
172 </DialogTitle>
173 <DialogContent dividers>
174 <Grid container spacing={2} sx={{mt: 1}}>
175 {data.userBuilds.map((build: any) => (
176 <Grid item xs={12} sm={6} md={4} key={build.id}>
177 <BuildCard
178 build={build}
179 onDelete={() => handleDelete(build.id)}
180 isOwner={true}
181 />
182 </Grid>
183 ))}
184 </Grid>
185 </DialogContent>
186
187 <DialogActions>
188 <Button onClick={() => setOpenMyBuildsDialog(false)}>Close</Button>
189 </DialogActions>
190 </Dialog>
191
192 </Grid>
193 </Grid>
194 </Grid>
195 </Container>
196 );
197}
198
199function BuildCard({build, onDelete, isOwner}: { build: any, onDelete?: () => void, isOwner?: boolean }) {
200 return (
201 <Card sx={{height: '100%', display: 'flex', flexDirection: 'column'}}>
202 <CardActionArea href={`/build/${build.id}`} sx={{flexGrow: 1}}>
203 <CardContent>
204 <Typography variant="subtitle1" fontWeight="bold" noWrap>
205 {build.name || `Build #${build.id}`}
206 </Typography>
207 <Typography variant="body2" color="text.secondary">
208 Created: {build.createdAt ? new Date(build.createdAt).toLocaleDateString() : 'Unknown'}
209 </Typography>
210 </CardContent>
211 </CardActionArea>
212
213 {isOwner && (
214 <Box sx={{display: 'flex', justifyContent: 'flex-end', p: 1, borderTop: '1px solid #eee'}}>
215 <IconButton size="small" href={`/build/edit/${build.id}`} color="primary">
216 <EditIcon fontSize="small"/>
217 </IconButton>
218 <IconButton size="small" onClick={onDelete} color="error">
219 <DeleteIcon fontSize="small"/>
220 </IconButton>
221 </Box>
222 )}
223 </Card>
224 );
225}
Note: See TracBrowser for help on using the repository browser.