Ignore:
File:
1 edited

Legend:

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

    r87b79bc r546a194  
    11import React, {useEffect, useState} from 'react';
    22import {
    3     Container, Grid, Paper, Typography, Box, Avatar, Divider, Button,
     3    Container, 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             <Grid container spacing={4}>
    176                 <Grid item xs={12} md={3}>
     175            <Box sx={{
     176                display: 'grid',
     177                gridTemplateColumns: {
     178                    xs: '1fr',
     179                    md: '300px 1fr'
     180                },
     181                gap: 4
     182            }}>
     183                <Box>
    177184                    <Paper elevation={3} sx={{
    178185                        p: 4,
     
    216223                        </Box>
    217224                    </Paper>
    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'}
     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                                                        }
     283                                                    />
     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' ? (
     304                                                        <Button
    263305                                                            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)}
    335                                                     />
    336                                                     <Box sx={{
    337                                                         mt: 1,
    338                                                         display: 'flex',
    339                                                         gap: 1,
    340                                                         justifyContent: 'center'
    341                                                     }}>
    342                                                         <Button
    343306                                                            variant="contained"
    344307                                                            color="warning"
    345                                                             size="small"
    346                                                             startIcon={<BuildIcon/>}
    347                                                             onClick={(e) => {
    348                                                                 e.stopPropagation();
    349                                                                 openBuildApproval(build.id, build.name);
    350                                                             }}
     308                                                            startIcon={<AddIcon/>}
     309                                                            onClick={() => setCreateComponentDialog({
     310                                                                open: true,
     311                                                                suggestion: sug
     312                                                            })}
    351313                                                        >
    352                                                             Review
     314                                                            Create
    353315                                                        </Button>
    354                                                     </Box>
    355                                                 </Box>
    356                                             </Grid>
     316                                                    ) : (
     317                                                        <Typography variant="caption" color="text.secondary">
     318                                                            {sug.adminComment || 'Rejected'}
     319                                                        </Typography>
     320                                                    )}
     321                                                </TableCell>
     322                                            </TableRow>
    357323                                        ))}
    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>
     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>
    404422
    405423            <Dialog open={suggestionDialog.open}
     
    520538            <AddComponentDialog
    521539                open={createComponentDialog.open}
    522                 onClose={() => setCreateComponentDialog({ open: false, suggestion: null })}
     540                onClose={() => setCreateComponentDialog({open: false, suggestion: null})}
    523541                onSuccess={() => {
    524542                    loadData();
    525                     setCreateComponentDialog({ open: false, suggestion: null });
     543                    setCreateComponentDialog({open: false, suggestion: null});
    526544                }}
    527545                prefillData={createComponentDialog.suggestion ? {
Note: See TracChangeset for help on using the changeset viewer.