Changes in pages/dashboard/admin/+Page.tsx [546a194:87b79bc]
- File:
-
- 1 edited
-
pages/dashboard/admin/+Page.tsx (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
pages/dashboard/admin/+Page.tsx
r546a194 r87b79bc 1 1 import React, {useEffect, useState} from 'react'; 2 2 import { 3 Container, Paper, Typography, Box, Avatar, Divider, Button,3 Container, Grid, Paper, Typography, Box, Avatar, Divider, Button, 4 4 CircularProgress, Table, TableBody, TableCell, TableHead, TableRow, 5 5 Chip, IconButton, TextField, Dialog, DialogTitle, DialogContent, DialogActions … … 173 173 return ( 174 174 <Container maxWidth="xl" sx={{mt: 4, mb: 10}}> 175 <Box sx={{ 176 display: 'grid', 177 gridTemplateColumns: { 178 xs: '1fr', 179 md: '300px 1fr' 180 }, 181 gap: 4 182 }}> 183 <Box> 175 <Grid container spacing={4}> 176 <Grid item xs={12} md={3}> 184 177 <Paper elevation={3} sx={{ 185 178 p: 4, … … 223 216 </Box> 224 217 </Paper> 225 </Box> 226 227 <Box sx={{ 228 display: 'flex', 229 flexDirection: 'column', 230 gap: 4 231 }}> 232 <Paper sx={{p: 3, borderLeft: '6px solid #9c27b0'}}> 233 <Box sx={{display: 'flex', alignItems: 'center', mb: 2}}> 234 <MemoryIcon color="secondary" sx={{mr: 1}}/> 235 <Typography variant="h6" fontWeight="bold"> 236 Component Suggestions ({data.componentSuggestions?.length || 0}) 237 </Typography> 238 </Box> 239 240 {data.componentSuggestions?.length === 0 ? ( 241 <Typography color="text.secondary">No suggestions.</Typography> 242 ) : ( 243 <Box sx={{width: '100%', overflowX: 'auto'}}> 244 <Table size="small"> 245 <TableHead> 246 <TableRow> 247 <TableCell>Link/Description</TableCell> 248 <TableCell>Type</TableCell> 249 <TableCell>User</TableCell> 250 <TableCell>Status</TableCell> 251 <TableCell align="right">Actions</TableCell> 252 </TableRow> 253 </TableHead> 254 <TableBody> 255 {data.componentSuggestions?.map((sug: any) => ( 256 <TableRow key={sug.id}> 257 <TableCell sx={{minWidth: 200, maxWidth: 300}}> 258 <a 259 href={sug.link} 260 target="_blank" 261 rel="noopener noreferrer" 262 title={sug.link} 263 style={{textDecoration: 'none', color: 'inherit'}} 264 > 265 {sug.description || sug.link} 266 </a> 267 </TableCell> 268 <TableCell sx={{minWidth: 80}}> 269 {sug.componentType?.toUpperCase()} 270 </TableCell> 271 <TableCell sx={{minWidth: 100}}> 272 {sug.user?.username || `${sug.userId}`} 273 </TableCell> 274 <TableCell sx={{minWidth: 100}}> 275 <Chip 276 label={sug.status || 'pending'} 277 size="small" 278 color={ 279 sug.status === 'approved' ? 'success' : 280 sug.status === 'rejected' ? 'error' : 281 'default' 282 } 218 </Grid> 219 220 <Grid item xs={12} md={9}> 221 <Grid container spacing={4} direction="column"> 222 <Grid item xs={12}> 223 <Paper sx={{p: 3, borderLeft: '6px solid #9c27b0'}}> 224 <Box sx={{display: 'flex', alignItems: 'center', mb: 2}}> 225 <MemoryIcon color="secondary" sx={{mr: 1}}/> 226 <Typography variant="h6" fontWeight="bold"> 227 Component Suggestions ({data.componentSuggestions?.length || 0}) 228 </Typography> 229 </Box> 230 231 {data.componentSuggestions?.length === 0 ? ( 232 <Typography color="text.secondary">No suggestions.</Typography> 233 ) : ( 234 <Table size="small"> 235 <TableHead> 236 <TableRow> 237 <TableCell>Link/Description</TableCell> 238 <TableCell>Type</TableCell> 239 <TableCell>User</TableCell> 240 <TableCell>Status</TableCell> 241 <TableCell align="right">Actions</TableCell> 242 </TableRow> 243 </TableHead> 244 <TableBody> 245 {data.componentSuggestions?.map((sug: any) => ( 246 <TableRow key={sug.id}> 247 <TableCell sx={{maxWidth: 300}}> 248 <a 249 href={sug.link} 250 target="_blank" 251 rel="noopener noreferrer" 252 title={sug.link} 253 style={{textDecoration: 'none', color: 'inherit'}} 254 > 255 {sug.description || sug.link} 256 </a> 257 </TableCell> 258 <TableCell>{sug.componentType?.toUpperCase()}</TableCell> 259 <TableCell>{sug.userId}</TableCell> 260 <TableCell> 261 <Chip 262 label={sug.status || 'pending'} 263 size="small" 264 color={ 265 sug.status === 'approved' ? 'success' : 266 sug.status === 'rejected' ? 'error' : 267 'default' 268 } 269 /> 270 </TableCell> 271 <TableCell align="right"> 272 {sug.status === 'pending' ? ( 273 <> 274 <IconButton 275 size="small" 276 color="success" 277 onClick={() => openSuggestionReview(sug.id, 'approved')} 278 > 279 <CheckCircleIcon/> 280 </IconButton> 281 <IconButton 282 size="small" 283 color="error" 284 onClick={() => openSuggestionReview(sug.id, 'rejected')} 285 > 286 <CancelIcon/> 287 </IconButton> 288 </> 289 ) : sug.status === 'approved' ? ( 290 <Button 291 size="small" 292 variant="contained" 293 color="warning" 294 startIcon={<AddIcon/>} 295 onClick={() => setCreateComponentDialog({ 296 open: true, 297 suggestion: sug 298 })} 299 > 300 Create 301 </Button> 302 ) : ( 303 <Typography variant="caption" color="text.secondary"> 304 {sug.adminComment || 'Rejected'} 305 </Typography> 306 )} 307 </TableCell> 308 </TableRow> 309 ))} 310 </TableBody> 311 </Table> 312 )} 313 </Paper> 314 </Grid> 315 316 <Grid item xs={12}> 317 <Paper sx={{p: 3, borderLeft: '6px solid #ed6c02', bgcolor: '#121212'}}> 318 <Box sx={{display: 'flex', alignItems: 'center', mb: 2}}> 319 <BuildIcon color="warning" sx={{mr: 1}}/> 320 <Typography variant="h6" fontWeight="bold"> 321 Builds Waiting for Approval ({data.pendingBuilds?.length || 0}) 322 </Typography> 323 </Box> 324 325 {data.pendingBuilds?.length === 0 ? ( 326 <Typography color="text.secondary">No pending builds.</Typography> 327 ) : ( 328 <Grid container spacing={2}> 329 {data.pendingBuilds.map((build: any) => ( 330 <Grid item xs={12} sm={6} md={4} lg={3} key={build.id}> 331 <Box sx={{position: 'relative'}}> 332 <BuildCard 333 build={build} 334 onClick={() => setSelectedBuildId(build.id)} 283 335 /> 284 </TableCell> 285 <TableCell align="right" sx={{minWidth: 150}}> 286 {sug.status === 'pending' ? ( 287 <> 288 <IconButton 289 size="small" 290 color="success" 291 onClick={() => openSuggestionReview(sug.id, 'approved')} 292 > 293 <CheckCircleIcon/> 294 </IconButton> 295 <IconButton 296 size="small" 297 color="error" 298 onClick={() => openSuggestionReview(sug.id, 'rejected')} 299 > 300 <CancelIcon/> 301 </IconButton> 302 </> 303 ) : sug.status === 'approved' ? ( 336 <Box sx={{ 337 mt: 1, 338 display: 'flex', 339 gap: 1, 340 justifyContent: 'center' 341 }}> 304 342 <Button 305 size="small"306 343 variant="contained" 307 344 color="warning" 308 startIcon={<AddIcon/>} 309 onClick={() => setCreateComponentDialog({ 310 open: true, 311 suggestion: sug 312 })} 345 size="small" 346 startIcon={<BuildIcon/>} 347 onClick={(e) => { 348 e.stopPropagation(); 349 openBuildApproval(build.id, build.name); 350 }} 313 351 > 314 Create352 Review 315 353 </Button> 316 ) : ( 317 <Typography variant="caption" color="text.secondary"> 318 {sug.adminComment || 'Rejected'} 319 </Typography> 320 )} 321 </TableCell> 322 </TableRow> 354 </Box> 355 </Box> 356 </Grid> 323 357 ))} 324 </TableBody> 325 </Table> 326 </Box> 327 )} 328 </Paper> 329 330 <Paper sx={{p: 3, borderLeft: '6px solid #ed6c02', bgcolor: '#121212'}}> 331 <Box sx={{display: 'flex', alignItems: 'center', mb: 2}}> 332 <BuildIcon color="warning" sx={{mr: 1}}/> 333 <Typography variant="h6" fontWeight="bold"> 334 Builds Waiting for Approval ({data.pendingBuilds?.length || 0}) 335 </Typography> 336 </Box> 337 338 {data.pendingBuilds?.length === 0 ? ( 339 <Typography color="text.secondary">No pending builds.</Typography> 340 ) : ( 341 <Box sx={{ 342 display: 'grid', 343 gridTemplateColumns: { 344 xs: '1fr', 345 sm: 'repeat(2, 1fr)', 346 lg: 'repeat(3, 1fr)', 347 xl: 'repeat(4, 1fr)' 348 }, 349 gap: 2 350 }}> 351 {data.pendingBuilds.map((build: any) => ( 352 <Box key={build.id}> 353 <BuildCard 354 build={build} 355 onClick={() => setSelectedBuildId(build.id)} 356 /> 357 <Box sx={{mt: 1, display: 'flex', gap: 1, justifyContent: 'center'}}> 358 <Button 359 variant="contained" 360 color="warning" 361 size="small" 362 startIcon={<BuildIcon/>} 363 onClick={(e) => { 364 e.stopPropagation(); 365 openBuildApproval(build.id, build.name); 366 }} 367 > 368 Review 369 </Button> 370 </Box> 371 </Box> 372 ))} 373 </Box> 374 )} 375 </Paper> 376 377 <Paper sx={{p: 3, borderLeft: '6px solid #2e7d32'}}> 378 <Typography variant="h6" fontWeight="bold" gutterBottom> 379 My Builds / Sandbox 380 </Typography> 381 {data.userBuilds?.length === 0 ? ( 382 <Typography color="text.secondary">No builds created by admin.</Typography> 383 ) : ( 384 <Box sx={{ 385 display: 'grid', 386 gridTemplateColumns: { 387 xs: '1fr', 388 sm: 'repeat(2, 1fr)', 389 lg: 'repeat(3, 1fr)', 390 xl: 'repeat(4, 1fr)' 391 }, 392 gap: 2 393 }}> 394 {data.userBuilds.slice(0, 4).map((build: any) => ( 395 <Box key={build.id}> 396 <BuildCard 397 build={build} 398 onClick={() => setSelectedBuildId(build.id)} 399 /> 400 <Box sx={{mt: 1, display: 'flex', justifyContent: 'center'}}> 401 <Button 402 variant="outlined" 403 color="error" 404 size="small" 405 startIcon={<DeleteIcon/>} 406 onClick={(e) => { 407 e.stopPropagation(); 408 openDeleteDialog(build.id, build.name); 409 }} 410 fullWidth 411 > 412 Delete 413 </Button> 414 </Box> 415 </Box> 416 ))} 417 </Box> 418 )} 419 </Paper> 420 </Box> 421 </Box> 358 </Grid> 359 )} 360 </Paper> 361 </Grid> 362 363 <Grid item xs={12}> 364 <Paper sx={{p: 3, borderLeft: '6px solid #2e7d32'}}> 365 <Typography variant="h6" fontWeight="bold" gutterBottom> 366 My Builds / Sandbox 367 </Typography> 368 {data.userBuilds?.length === 0 ? ( 369 <Typography color="text.secondary">No builds created by admin.</Typography> 370 ) : ( 371 <Grid container spacing={2}> 372 {data.userBuilds.slice(0, 4).map((build: any) => ( 373 <Grid item xs={12} sm={6} md={4} lg={3} key={build.id}> 374 <Box sx={{position: 'relative'}}> 375 <BuildCard 376 build={build} 377 onClick={() => setSelectedBuildId(build.id)} 378 /> 379 <Box sx={{mt: 1, display: 'flex', justifyContent: 'center'}}> 380 <Button 381 variant="outlined" 382 color="error" 383 size="small" 384 startIcon={<DeleteIcon/>} 385 onClick={(e) => { 386 e.stopPropagation(); 387 openDeleteDialog(build.id, build.name); 388 }} 389 fullWidth 390 > 391 Delete 392 </Button> 393 </Box> 394 </Box> 395 </Grid> 396 ))} 397 </Grid> 398 )} 399 </Paper> 400 </Grid> 401 </Grid> 402 </Grid> 403 </Grid> 422 404 423 405 <Dialog open={suggestionDialog.open} … … 538 520 <AddComponentDialog 539 521 open={createComponentDialog.open} 540 onClose={() => setCreateComponentDialog({ open: false, suggestion: null})}522 onClose={() => setCreateComponentDialog({ open: false, suggestion: null })} 541 523 onSuccess={() => { 542 524 loadData(); 543 setCreateComponentDialog({ open: false, suggestion: null});525 setCreateComponentDialog({ open: false, suggestion: null }); 544 526 }} 545 527 prefillData={createComponentDialog.suggestion ? {
Note:
See TracChangeset
for help on using the changeset viewer.
