Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • pages/dashboard/admin/+Page.tsx

    r546a194 r87b79bc  
    11import React, {useEffect, useState} from 'react';
    22import {
    3     Container, Paper, Typography, Box, Avatar, Divider, Button,
     3    Container, Grid, Paper, Typography, Box, Avatar, Divider, Button,
    44    CircularProgress, Table, TableBody, TableCell, TableHead, TableRow,
    55    Chip, IconButton, TextField, Dialog, DialogTitle, DialogContent, DialogActions
     
    173173    return (
    174174        <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}>
    184177                    <Paper elevation={3} sx={{
    185178                        p: 4,
     
    223216                        </Box>
    224217                    </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)}
    283335                                                    />
    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                                                    }}>
    304342                                                        <Button
    305                                                             size="small"
    306343                                                            variant="contained"
    307344                                                            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                                                            }}
    313351                                                        >
    314                                                             Create
     352                                                            Review
    315353                                                        </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>
    323357                                        ))}
    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>
    422404
    423405            <Dialog open={suggestionDialog.open}
     
    538520            <AddComponentDialog
    539521                open={createComponentDialog.open}
    540                 onClose={() => setCreateComponentDialog({open: false, suggestion: null})}
     522                onClose={() => setCreateComponentDialog({ open: false, suggestion: null })}
    541523                onSuccess={() => {
    542524                    loadData();
    543                     setCreateComponentDialog({open: false, suggestion: null});
     525                    setCreateComponentDialog({ open: false, suggestion: null });
    544526                }}
    545527                prefillData={createComponentDialog.suggestion ? {
Note: See TracChangeset for help on using the changeset viewer.