Changeset 5af32f0 for pages/dashboard
- Timestamp:
- 12/29/25 01:16:09 (6 months ago)
- Branches:
- main
- Children:
- 915ce0f
- Parents:
- 8a7f936
- File:
-
- 1 edited
-
pages/dashboard/admin/+Page.tsx (modified) (13 diffs)
Legend:
- Unmodified
- Added
- Removed
-
pages/dashboard/admin/+Page.tsx
r8a7f936 r5af32f0 1 import React, { useEffect, useState} from 'react';1 import React, {useEffect, useState} from 'react'; 2 2 import { 3 3 Container, Grid, Paper, Typography, Box, Avatar, Divider, Button, … … 25 25 const [selectedBuildId, setSelectedBuildId] = useState<number | null>(null); 26 26 27 const [suggestionDialog, setSuggestionDialog] = useState<{ open: boolean, id: number | null, action: 'approved' | 'rejected' }>({ open: false, id: null, action: 'approved' }); 27 // Build approval dialog state 28 const [buildApprovalDialog, setBuildApprovalDialog] = useState<{ 29 open: boolean, 30 buildId: number | null, 31 buildName: string 32 }>({open: false, buildId: null, buildName: ''}); 33 const [rejectReason, setRejectReason] = useState(''); 34 const [approvalLoading, setApprovalLoading] = useState(false); 35 36 const [suggestionDialog, setSuggestionDialog] = useState<{ 37 open: boolean, 38 id: number | null, 39 action: 'approved' | 'rejected' 40 }>({open: false, id: null, action: 'approved'}); 28 41 const [adminComment, setAdminComment] = useState(""); 29 42 30 43 const loadData = () => { 31 44 getAdminInfoAndData() 32 .then(res => { setData(res); setLoading(false); }) 45 .then(res => { 46 setData(res); 47 setLoading(false); 48 }) 33 49 .catch(err => { 34 50 console.error(err); … … 37 53 }; 38 54 39 useEffect(() => { loadData(); }, []); 40 41 const handleBuildReview = async (buildId: number, isApproved: boolean) => { 42 if (!confirm(`Are you sure you want to ${isApproved ? 'APPROVE' : 'REJECT'} this build?`)) return; 55 useEffect(() => { 56 loadData(); 57 }, []); 58 59 const openBuildApproval = (buildId: number, buildName: string) => { 60 setBuildApprovalDialog({open: true, buildId, buildName}); 61 setRejectReason(''); 62 }; 63 64 const handleApproveBuild = async () => { 65 if (!buildApprovalDialog.buildId) return; 66 setApprovalLoading(true); 43 67 try { 44 await onSetBuildApprovalStatus({ buildId, isApproved }); 68 await onSetBuildApprovalStatus({buildId: buildApprovalDialog.buildId, isApproved: true}); 69 setBuildApprovalDialog({open: false, buildId: null, buildName: ''}); 45 70 loadData(); 46 } catch (e) { alert("Action failed"); } 71 } catch (e) { 72 alert("Approve failed"); 73 } finally { 74 setApprovalLoading(false); 75 } 76 }; 77 78 const handleRejectBuild = async () => { 79 if (!buildApprovalDialog.buildId || !rejectReason.trim()) return; 80 setApprovalLoading(true); 81 try { 82 await onSetBuildApprovalStatus({buildId: buildApprovalDialog.buildId, isApproved: false}); 83 setBuildApprovalDialog({open: false, buildId: null, buildName: ''}); 84 loadData(); 85 } catch (e) { 86 alert("Reject failed"); 87 } finally { 88 setApprovalLoading(false); 89 } 47 90 }; 48 91 49 92 const openSuggestionReview = (id: number, action: 'approved' | 'rejected') => { 50 setSuggestionDialog({ open: true, id, action});93 setSuggestionDialog({open: true, id, action}); 51 94 setAdminComment(action === 'approved' ? "" : ""); 52 95 }; … … 60 103 adminComment: adminComment 61 104 }); 62 // console.error("Test za admincomment: ", adminComment); 63 setSuggestionDialog({ ...suggestionDialog, open: false }); 105 setSuggestionDialog({...suggestionDialog, open: false}); 64 106 loadData(); 65 } catch (e) { alert("Failed to update suggestion"); } 66 }; 67 68 if (loading) return <Box sx={{ p: 5, textAlign: 'center' }}><CircularProgress /></Box>; 69 if (!data) return <Box sx={{ p: 5, textAlign: 'center' }}>Access Denied</Box>; 107 } catch (e) { 108 alert("Failed to update suggestion"); 109 } 110 }; 111 112 if (loading) return <Box sx={{p: 5, textAlign: 'center'}}><CircularProgress/></Box>; 113 if (!data) return <Box sx={{p: 5, textAlign: 'center'}}>Access Denied</Box>; 70 114 71 115 return ( 72 <Container maxWidth="xl" sx={{ mt: 4, mb: 10}}>116 <Container maxWidth="xl" sx={{mt: 4, mb: 10}}> 73 117 <Grid container spacing={4}> 74 118 <Grid item xs={12} md={3}> 75 <Paper elevation={3} sx={{ p: 4, display: 'flex', flexDirection: 'column', alignItems: 'center', height: '100%', bgcolor: '#1e1e1e', color: 'white' }}> 76 <Avatar sx={{ width: 120, height: 120, mb: 2, bgcolor: 'error.main' }}> 77 <AdminPanelSettingsIcon sx={{ fontSize: 70 }} /> 119 <Paper elevation={3} sx={{ 120 p: 4, 121 display: 'flex', 122 flexDirection: 'column', 123 alignItems: 'center', 124 height: '100%', 125 bgcolor: '#1e1e1e', 126 color: 'white' 127 }}> 128 <Avatar sx={{width: 120, height: 120, mb: 2, bgcolor: 'error.main'}}> 129 <AdminPanelSettingsIcon sx={{fontSize: 70}}/> 78 130 </Avatar> 79 131 <Typography variant="h5" fontWeight="bold">{data.admin.username}</Typography> 80 <Typography variant="body2" sx={{ opacity: 0.7, mb: 3}}>{data.admin.email}</Typography>81 82 <Chip label="ADMINISTRATOR" color="error" variant="outlined" sx={{ fontWeight: 'bold' }}/>83 <Divider sx={{ width: '100%', my: 3, bgcolor: 'rgba(255,255,255,0.1)' }}/>84 85 <Box sx={{ width: '100%', textAlign: 'center'}}>132 <Typography variant="body2" sx={{opacity: 0.7, mb: 3}}>{data.admin.email}</Typography> 133 134 <Chip label="ADMINISTRATOR" color="error" variant="outlined" sx={{fontWeight: 'bold'}}/> 135 <Divider sx={{width: '100%', my: 3, bgcolor: 'rgba(255,255,255,0.1)'}}/> 136 137 <Box sx={{width: '100%', textAlign: 'center'}}> 86 138 <Typography variant="h3" fontWeight="bold" color="primary.main"> 87 139 {data.pendingBuilds?.length || 0} … … 95 147 <Grid container spacing={4} direction="column"> 96 148 <Grid item xs={12}> 97 <Paper sx={{ p: 3, borderLeft: '6px solid #9c27b0'}}>98 <Box sx={{ display: 'flex', alignItems: 'center', mb: 2}}>99 <MemoryIcon color="secondary" sx={{ mr: 1 }}/>149 <Paper sx={{p: 3, borderLeft: '6px solid #9c27b0'}}> 150 <Box sx={{display: 'flex', alignItems: 'center', mb: 2}}> 151 <MemoryIcon color="secondary" sx={{mr: 1}}/> 100 152 <Typography variant="h6" fontWeight="bold"> 101 153 Suggested Components ({data.componentSuggestions?.length || 0}) … … 118 170 {data.componentSuggestions.map((sug: any) => ( 119 171 <TableRow key={sug.id}> 120 <TableCell sx={{ maxWidth: 300 }}> 121 <a href={sug.link} title={sug.link} style={{textDecoration: 'none', color: 'inherit'}}> 172 <TableCell sx={{maxWidth: 300}}> 173 <a href={sug.link} title={sug.link} 174 style={{textDecoration: 'none', color: 'inherit'}}> 122 175 {sug.description || sug.link} 123 176 </a> … … 126 179 <TableCell>{sug.userId}</TableCell> 127 180 <TableCell align="right"> 128 <IconButton size="small" color="success" onClick={() => openSuggestionReview(sug.id, 'approved')}> 129 <CheckCircleIcon /> 181 <IconButton size="small" color="success" 182 onClick={() => openSuggestionReview(sug.id, 'approved')}> 183 <CheckCircleIcon/> 130 184 </IconButton> 131 <IconButton size="small" color="error" onClick={() => openSuggestionReview(sug.id, 'rejected')}> 132 <CancelIcon /> 185 <IconButton size="small" color="error" 186 onClick={() => openSuggestionReview(sug.id, 'rejected')}> 187 <CancelIcon/> 133 188 </IconButton> 134 189 </TableCell> … … 142 197 143 198 <Grid item xs={12}> 144 <Paper sx={{ p: 3, borderLeft: '6px solid #ed6c02', bgcolor: '#121212'}}>145 <Box sx={{ display: 'flex', alignItems: 'center', mb: 2}}>146 <BuildIcon color="warning" sx={{ mr: 1 }}/>199 <Paper sx={{p: 3, borderLeft: '6px solid #ed6c02', bgcolor: '#121212'}}> 200 <Box sx={{display: 'flex', alignItems: 'center', mb: 2}}> 201 <BuildIcon color="warning" sx={{mr: 1}}/> 147 202 <Typography variant="h6" fontWeight="bold"> 148 203 Builds Waiting for Approval ({data.pendingBuilds?.length || 0}) … … 156 211 {data.pendingBuilds.map((build: any) => ( 157 212 <Grid item xs={12} md={4} key={build.id}> 158 <Box sx={{ position: 'relative'}}>213 <Box sx={{position: 'relative'}}> 159 214 <BuildCard 160 215 build={build} … … 162 217 /> 163 218 <Box sx={{ 164 mt: 1, display: 'flex', gap: 1, 165 justifyContent: 'center', bgcolor: '#121212', p: 1, borderRadius: 1 219 mt: 1, 220 display: 'flex', 221 gap: 1, 222 justifyContent: 'center', 223 bgcolor: '#292929', 224 p: 1, 225 borderRadius: 1 166 226 }}> 167 <Button variant="contained" color="success" size="small" onClick={() => handleBuildReview(build.id, true)}> 168 Approve 169 </Button> 170 <Button variant="contained" color="error" size="small" onClick={() => handleBuildReview(build.id, false)}> 171 Reject 227 <Button 228 variant="contained" 229 color="warning" 230 size="small" 231 startIcon={<BuildIcon/>} 232 onClick={() => openBuildApproval(build.id, build.name)} 233 > 234 Review 172 235 </Button> 173 236 </Box> … … 181 244 182 245 <Grid item xs={12}> 183 <Paper sx={{ p: 3, borderLeft: '6px solid #2e7d32'}}>246 <Paper sx={{p: 3, borderLeft: '6px solid #2e7d32'}}> 184 247 <Typography variant="h6" fontWeight="bold" gutterBottom> 185 248 My Builds / Sandbox … … 201 264 </Paper> 202 265 </Grid> 203 204 266 </Grid> 205 267 </Grid> 206 268 </Grid> 207 269 208 <Dialog open={suggestionDialog.open} onClose={() => setSuggestionDialog({...suggestionDialog, open: false})}> 270 <Dialog open={suggestionDialog.open} 271 onClose={() => setSuggestionDialog({...suggestionDialog, open: false})}> 209 272 <DialogTitle>Review Component Suggestion</DialogTitle> 210 273 <DialogContent> … … 221 284 </Dialog> 222 285 286 <Dialog open={buildApprovalDialog.open} 287 onClose={() => setBuildApprovalDialog({open: false, buildId: null, buildName: ''})}> 288 <DialogTitle>Review Build</DialogTitle> 289 <DialogContent> 290 <Typography variant="h6" gutterBottom>{buildApprovalDialog.buildName}</Typography> 291 <Typography color="text.secondary" sx={{mb: 2}}> 292 Build ID: {buildApprovalDialog.buildId} 293 </Typography> 294 <TextField 295 fullWidth 296 multiline 297 rows={2} 298 label="Reject reason (optional)" 299 value={rejectReason} 300 onChange={(e) => setRejectReason(e.target.value)} 301 placeholder="Why reject this build?" 302 sx={{mt: 1}} 303 /> 304 </DialogContent> 305 <DialogActions> 306 <Button 307 onClick={() => setBuildApprovalDialog({open: false, buildId: null, buildName: ''})} 308 disabled={approvalLoading} 309 > 310 Cancel 311 </Button> 312 <Button 313 onClick={handleRejectBuild} 314 variant="outlined" 315 color="error" 316 disabled={approvalLoading || !rejectReason.trim()} 317 > 318 {approvalLoading ? <CircularProgress size={20}/> : 'Reject'} 319 </Button> 320 <Button 321 onClick={handleApproveBuild} 322 variant="contained" 323 color="success" 324 disabled={approvalLoading} 325 > 326 {approvalLoading ? <CircularProgress size={20}/> : 'Approve'} 327 </Button> 328 </DialogActions> 329 </Dialog> 330 223 331 <BuildDetailsDialog 224 332 open={!!selectedBuildId}
Note:
See TracChangeset
for help on using the changeset viewer.
