Changeset 958bc89 for pages/dashboard


Ignore:
Timestamp:
12/29/25 04:34:44 (6 months ago)
Author:
Mihail <mihail2.naumov@…>
Branches:
main
Children:
41a2f81
Parents:
f46bf5c
Message:

Added add component button, fixed several things

Location:
pages/dashboard/admin
Files:
2 edited

Legend:

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

    rf46bf5c r958bc89  
    1010import BuildIcon from '@mui/icons-material/Build';
    1111import MemoryIcon from '@mui/icons-material/Memory';
     12import AddIcon from '@mui/icons-material/Add';
     13import DeleteIcon from '@mui/icons-material/Delete';
    1214
    1315import {
     
    1921import BuildCard from '../../../components/BuildCard';
    2022import BuildDetailsDialog from '../../../components/BuildDetailsDialog';
    21 import DeleteIcon from "@mui/icons-material/Delete";
    2223import {onDeleteBuild} from "../user/userDashboard.telefunc";
     24import AddComponentDialog from "../../../components/AddComponentDialog";
    2325
    2426export default function AdminDashboard() {
     
    2628    const [loading, setLoading] = useState(true);
    2729    const [selectedBuildId, setSelectedBuildId] = useState<number | null>(null);
    28     const [deleteDialog, setDeleteDialog] = useState<{ open: boolean, buildId: number | null, buildName: string }>({ open: false, buildId: null, buildName: '' });
     30    const [deleteDialog, setDeleteDialog] = useState<{
     31        open: boolean,
     32        buildId: number | null,
     33        buildName: string
     34    }>({open: false, buildId: null, buildName: ''});
    2935    const [deleteLoading, setDeleteLoading] = useState(false);
     36    const [addDialogOpen, setAddDialogOpen] = useState(false);
    3037
    3138    const [buildApprovalDialog, setBuildApprovalDialog] = useState<{
     
    4552
    4653    const loadData = () => {
     54        setLoading(true);
    4755        getAdminInfoAndData()
    4856            .then(res => {
     
    5361                console.error(err);
    5462                alert("Failed to load admin data. Are you an admin?");
     63                setLoading(false);
    5564            });
    5665    };
     
    114123
    115124    const openDeleteDialog = (buildId: number, buildName: string) => {
    116         setDeleteDialog({ open: true, buildId, buildName });
     125        setDeleteDialog({open: true, buildId, buildName});
    117126    };
    118127
     
    122131        try {
    123132            await onDeleteBuild({buildId: deleteDialog.buildId});
    124             setDeleteDialog({ open: false, buildId: null, buildName: '' });
    125             loadData(); // refresh na user i favorite builds
     133            setDeleteDialog({open: false, buildId: null, buildName: ''});
     134            loadData();
    126135            setSelectedBuildId(null);
    127136        } catch (e) {
     
    132141    };
    133142
    134     if (loading) return <Box sx={{p: 5, textAlign: 'center'}}><CircularProgress/></Box>;
    135     if (!data) return <Box sx={{p: 5, textAlign: 'center'}}>Access Denied</Box>;
     143    if (loading) {
     144        return (
     145            <Container maxWidth="xl" sx={{mt: 4, mb: 10, display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '50vh'}}>
     146                <CircularProgress />
     147            </Container>
     148        );
     149    }
     150
     151    if (!data) {
     152        return (
     153            <Container maxWidth="xl" sx={{mt: 4, mb: 10, textAlign: 'center', p: 5}}>
     154                <Typography variant="h6" color="error">
     155                    Access Denied - Admin privileges required
     156                </Typography>
     157            </Container>
     158        );
     159    }
    136160
    137161    return (
     
    151175                            <AdminPanelSettingsIcon sx={{fontSize: 70}}/>
    152176                        </Avatar>
    153                         <Typography variant="h5" fontWeight="bold">{data.admin.username}</Typography>
    154                         <Typography variant="body2" sx={{opacity: 0.7, mb: 3}}>{data.admin.email}</Typography>
     177                        <Typography variant="h5" fontWeight="bold">{data.admin?.username || 'Admin'}</Typography>
     178                        <Typography variant="body2" sx={{opacity: 0.7, mb: 3}}>
     179                            {data.admin?.email || 'admin@example.com'}
     180                        </Typography>
    155181
    156182                        <Chip label="ADMINISTRATOR" color="error" variant="outlined" sx={{fontWeight: 'bold'}}/>
     
    162188                            </Typography>
    163189                            <Typography variant="caption">Pending Builds</Typography>
     190                        </Box>
     191
     192                        <Box sx={{width: '100%', textAlign: 'center', mt: 3}}>
     193                            <Button
     194                                variant="contained"
     195                                color="warning"
     196                                size="large"
     197                                fullWidth
     198                                startIcon={<BuildIcon />}
     199                                onClick={() => setAddDialogOpen(true)}
     200                                sx={{ mb: 2 }}
     201                            >
     202                                Add Component
     203                            </Button>
    164204                        </Box>
    165205                    </Paper>
     
    190230                                        </TableHead>
    191231                                        <TableBody>
    192                                             {data.componentSuggestions.map((sug: any) => (
     232                                            {data.componentSuggestions?.map((sug: any) => (
    193233                                                <TableRow key={sug.id}>
    194234                                                    <TableCell sx={{maxWidth: 300}}>
    195                                                         <a href={sug.link} title={sug.link}
    196                                                            style={{textDecoration: 'none', color: 'inherit'}}>
     235                                                        <a
     236                                                            href={sug.link}
     237                                                            target="_blank"
     238                                                            rel="noopener noreferrer"
     239                                                            title={sug.link}
     240                                                            style={{textDecoration: 'none', color: 'inherit'}}
     241                                                        >
    197242                                                            {sug.description || sug.link}
    198243                                                        </a>
    199244                                                    </TableCell>
    200                                                     <TableCell>{sug.componentType.toUpperCase()}</TableCell>
     245                                                    <TableCell>{sug.componentType?.toUpperCase()}</TableCell>
    201246                                                    <TableCell>{sug.userId}</TableCell>
    202247                                                    <TableCell align="right">
    203                                                         <IconButton size="small" color="success"
    204                                                                     onClick={() => openSuggestionReview(sug.id, 'approved')}>
     248                                                        <IconButton
     249                                                            size="small"
     250                                                            color="success"
     251                                                            onClick={() => openSuggestionReview(sug.id, 'approved')}
     252                                                        >
    205253                                                            <CheckCircleIcon/>
    206254                                                        </IconButton>
    207                                                         <IconButton size="small" color="error"
    208                                                                     onClick={() => openSuggestionReview(sug.id, 'rejected')}>
     255                                                        <IconButton
     256                                                            size="small"
     257                                                            color="error"
     258                                                            onClick={() => openSuggestionReview(sug.id, 'rejected')}
     259                                                        >
    209260                                                            <CancelIcon/>
    210261                                                        </IconButton>
     
    232283                                    <Grid container spacing={2}>
    233284                                        {data.pendingBuilds.map((build: any) => (
    234                                             <Grid item xs={12} md={4} key={build.id}>
     285                                            <Grid item xs={12} sm={6} md={4} lg={3} key={build.id}>
    235286                                                <Box sx={{position: 'relative'}}>
    236287                                                    <BuildCard
     
    242293                                                        display: 'flex',
    243294                                                        gap: 1,
    244                                                         justifyContent: 'center',
    245                                                         bgcolor: '#292929',
    246                                                         p: 1,
    247                                                         borderRadius: 1
     295                                                        justifyContent: 'center'
    248296                                                    }}>
    249297                                                        <Button
     
    251299                                                            color="warning"
    252300                                                            size="small"
    253                                                             startIcon={<BuildIcon/>}
    254                                                             onClick={() => openBuildApproval(build.id, build.name)}
     301                                                            startIcon={<BuildIcon />}
     302                                                            onClick={(e) => {
     303                                                                e.stopPropagation();
     304                                                                openBuildApproval(build.id, build.name);
     305                                                            }}
    255306                                                        >
    256307                                                            Review
     
    275326                                    <Grid container spacing={2}>
    276327                                        {data.userBuilds.slice(0, 4).map((build: any) => (
    277                                             <Grid item xs={12} sm={6} md={4} key={build.id}>
    278                                                 <Box sx={{ position: 'relative' }}>
     328                                            <Grid item xs={12} sm={6} md={4} lg={3} key={build.id}>
     329                                                <Box sx={{position: 'relative'}}>
    279330                                                    <BuildCard
    280331                                                        build={build}
    281332                                                        onClick={() => setSelectedBuildId(build.id)}
    282333                                                    />
    283                                                     <Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>
     334                                                    <Box sx={{mt: 1, display: 'flex', justifyContent: 'center'}}>
    284335                                                        <Button
    285336                                                            variant="outlined"
     
    291342                                                                openDeleteDialog(build.id, build.name);
    292343                                                            }}
    293                                                             sx={{ width: '100%' }}
     344                                                            fullWidth
    294345                                                        >
    295346                                                            Delete
     
    307358            </Grid>
    308359
    309             <Dialog open={suggestionDialog.open}
    310                     onClose={() => setSuggestionDialog({...suggestionDialog, open: false})}>
     360            <Dialog open={suggestionDialog.open} onClose={() => setSuggestionDialog({...suggestionDialog, open: false})}>
    311361                <DialogTitle>Review Component Suggestion</DialogTitle>
    312362                <DialogContent>
    313363                    <Typography gutterBottom>Status: <b>{suggestionDialog.action.toUpperCase()}</b></Typography>
    314364                    <TextField
    315                         fullWidth label="Admin Comment" multiline rows={3}
    316                         value={adminComment} onChange={(e) => setAdminComment(e.target.value)}
     365                        fullWidth
     366                        label="Admin Comment"
     367                        multiline
     368                        rows={3}
     369                        value={adminComment}
     370                        onChange={(e) => setAdminComment(e.target.value)}
     371                        sx={{ mt: 2 }}
    317372                    />
    318373                </DialogContent>
     
    323378            </Dialog>
    324379
    325             <Dialog open={buildApprovalDialog.open}
    326                     onClose={() => setBuildApprovalDialog({open: false, buildId: null, buildName: ''})}>
     380            <Dialog
     381                open={buildApprovalDialog.open}
     382                onClose={() => setBuildApprovalDialog({open: false, buildId: null, buildName: ''})}
     383            >
    327384                <DialogTitle>Review Build</DialogTitle>
    328385                <DialogContent>
     
    368425            </Dialog>
    369426
    370             <Dialog open={deleteDialog.open} onClose={() => setDeleteDialog({ open: false, buildId: null, buildName: '' })}>
     427            <Dialog
     428                open={deleteDialog.open}
     429                onClose={() => setDeleteDialog({open: false, buildId: null, buildName: ''})}
     430            >
    371431                <DialogTitle>Delete Build</DialogTitle>
    372432                <DialogContent>
     
    378438                <DialogActions>
    379439                    <Button
    380                         onClick={() => setDeleteDialog({ open: false, buildId: null, buildName: '' })}
     440                        onClick={() => setDeleteDialog({open: false, buildId: null, buildName: ''})}
    381441                        disabled={deleteLoading}
    382442                    >
     
    388448                        color="error"
    389449                        disabled={deleteLoading}
    390                         startIcon={deleteLoading ? <CircularProgress size={20} /> : <DeleteIcon />}
     450                        startIcon={deleteLoading ? <CircularProgress size={20}/> : <DeleteIcon />}
    391451                    >
    392452                        {deleteLoading ? 'Deleting...' : 'Delete Build'}
     
    397457            <BuildDetailsDialog
    398458                open={!!selectedBuildId}
    399                 buildId={selectedBuildId}
    400                 currentUser={data.admin?.id}
     459                buildId={selectedBuildId!}
     460                currentUser={data.admin}
    401461                onClose={() => setSelectedBuildId(null)}
    402                 onClone={() => alert("Clone disabled in admin view")}
    403462                isDashboardView={true}
     463            />
     464
     465            <AddComponentDialog
     466                open={addDialogOpen}
     467                onClose={() => setAddDialogOpen(false)}
     468                onSuccess={() => {
     469                    loadData();
     470                    setAddDialogOpen(false);
     471                }}
    404472            />
    405473        </Container>
  • pages/dashboard/admin/adminDashboard.telefunc.ts

    rf46bf5c r958bc89  
    11import * as drizzleQueries from "../../../database/drizzle/queries";
    2 import { requireAdmin } from "../../../server/telefunc/ctx";
     2import {requireAdmin} from "../../../server/telefunc/ctx";
    33import {Abort} from "telefunc";
    44import { validateComponentSpecificData } from "../../../database/drizzle/util/componentFieldConfig";
Note: See TracChangeset for help on using the changeset viewer.