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

main
Last change on this file since f7c0b0d was 03722c9, checked in by Tome <gjorgievtome@…>, 7 months ago

Fix line endings in docker-entrypoint.sh

  • Property mode set to 100644
File size: 10.4 KB
RevLine 
[40ac7a9]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 // @ts-ignore
73 return (
74 <Container maxWidth="xl" sx={{mt: 4, mb: 4, color: 'white'}}>
75 <Grid container spacing={3}>
76
77 <Grid item xs={12} md={3}>
78 <Paper elevation={3}
79 sx={{p: 3, display: 'flex', flexDirection: 'column', alignItems: 'center', height: '100%'}}>
80 <Avatar sx={{width: 100, height: 100, mb: 2, bgcolor: 'primary.main'}}><PersonIcon
81 sx={{fontSize: 60}}/></Avatar>
82 <Typography variant="h5" fontWeight="bold" gutterBottom>{data.user.username}</Typography>
83 <Typography variant="body2" color="text.secondary" gutterBottom>{data.user.email}</Typography>
84 <Divider sx={{width: '100%', my: 2}}/>
85 {/*<Button variant="outlined" fullWidth color="primary" sx={{mb: 1}}>Edit Profile</Button>*/}
86 </Paper>
87 </Grid>
88
89 <Grid item xs={12} md={9}>
90 <Grid container spacing={3} direction="column">
91
92 <Grid item xs={12}>
93 <Paper elevation={3} sx={{p: 3, minHeight: '300px', bgcolor: '#1e1e1e'}}>
94 <Box sx={{display: 'flex', alignItems: 'center', mb: 2}}>
95 <FavoriteIcon color="error" sx={{mr: 1}}/>
96 <Typography variant="h6" fontWeight="bold">Favorited Builds</Typography>
97 </Box>
98 <Divider sx={{mb: 2}}/>
99
100 {data.favoriteBuilds.length === 0 ? (
101 <Typography color="text.secondary" align="center" sx={{mt: 4}}>You haven't favorited
102 any builds yet.</Typography>
103 ) : (
104 <Grid container spacing={2}>
105 {data.favoriteBuilds.map((build: any) => (
106 <Grid item xs={12} sm={6} md={4} key={build.id}>
107 <BuildCard build={build}/>
108 </Grid>
109 ))}
110 </Grid>
111 )}
112 </Paper>
113 </Grid>
114
115 <Grid item xs={12}>
116 <Paper elevation={3} sx={{p: 3, minHeight: '300px', bgcolor: '#1e1e1e'}}>
117
118 <Box sx={{
119 display: 'flex',
120 alignItems: 'center',
121 justifyContent: 'space-between',
122 mb: 2
123 }}>
124 <Box sx={{display: 'flex', alignItems: 'center'}}>
125 <ComputerIcon color="primary" sx={{mr: 1}}/>
126 <Typography variant="h6" fontWeight="bold">My Builds</Typography>
127 </Box>
128
129 {data.userBuilds.length > 4 && (
130 <Button
131 variant="outlined"
132 size="small"
133 onClick={() => setOpenMyBuildsDialog(true)}
134 >
135 See All ({data.userBuilds.length})
136 </Button>
137 )}
138 </Box>
139 <Divider sx={{mb: 2}}/>
140
141 {data.userBuilds.length === 0 ? (
142 <Box sx={{textAlign: 'center', mt: 4}}>
143 <Typography color="text.secondary" gutterBottom>You haven't created any builds
144 yet.</Typography>
145 <Button variant="contained" href="/forge"
146 sx={{color: 'white', fontWeight: 'bolder'}}>Start Forging</Button>
147 </Box>
148 ) : (
149 <Grid container spacing={2}>
150 {data.userBuilds.slice(0, 3).map((build: any) => (
151 <Grid item xs={12} sm={6} md={3} key={build.id}>
152 <BuildCard
153 build={build}
154 onDelete={() => handleDelete(build.id)}
155 isOwner={true}
156 />
157 </Grid>
158 ))}
159 </Grid>
160 )}
161 </Paper>
162 </Grid>
163
164 <Dialog open={openMyBuildsDialog} onClose={() => setOpenMyBuildsDialog(false)} maxWidth="md"
165 fullWidth>
166 <DialogTitle sx={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>All
167 My Builds
168 <IconButton onClick={() => setOpenMyBuildsDialog(false)}>
169 <CloseIcon/>
170 </IconButton>
171 </DialogTitle>
172 <DialogContent dividers>
173 <Grid container spacing={2} sx={{mt: 1}}>
174 {data.userBuilds.map((build: any) => (
175 <Grid item xs={12} sm={6} md={4} key={build.id}>
176 <BuildCard
177 build={build}
178 onDelete={() => handleDelete(build.id)}
179 isOwner={true}
180 />
181 </Grid>
182 ))}
183 </Grid>
184 </DialogContent>
185
186 <DialogActions>
187 <Button onClick={() => setOpenMyBuildsDialog(false)}>Close</Button>
188 </DialogActions>
189 </Dialog>
190
191 </Grid>
192 </Grid>
193 </Grid>
194 </Container>
195 );
196}
197
198function BuildCard({build, onDelete, isOwner}: { build: any, onDelete?: () => void, isOwner?: boolean }) {
199 return (
200 <Card sx={{height: '100%', display: 'flex', flexDirection: 'column'}}>
201 <CardActionArea href={`/build/${build.id}`} sx={{flexGrow: 1}}>
202 <CardContent>
203 <Typography variant="subtitle1" fontWeight="bold" noWrap>
204 {build.name || `Build #${build.id}`}
205 </Typography>
206 <Typography variant="body2" color="text.secondary">
207 Created: {build.createdAt ? new Date(build.createdAt).toLocaleDateString() : 'Unknown'}
208 </Typography>
209 </CardContent>
210 </CardActionArea>
211
212 {isOwner && (
213 <Box sx={{display: 'flex', justifyContent: 'flex-end', p: 1, borderTop: '1px solid #eee'}}>
214 <IconButton size="small" href={`/build/edit/${build.id}`} color="primary">
215 <EditIcon fontSize="small"/>
216 </IconButton>
217 <IconButton size="small" onClick={onDelete} color="error">
218 <DeleteIcon fontSize="small"/>
219 </IconButton>
220 </Box>
221 )}
222 </Card>
223 );
224}
Note: See TracBrowser for help on using the repository browser.