Changeset a744c90


Ignore:
Timestamp:
01/28/26 12:13:13 (5 months ago)
Author:
Tome <gjorgievtome@…>
Branches:
main
Children:
1d8f088
Parents:
e03e6fb (diff), 87b79bc (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge remote-tracking branch 'origin/main'

Files:
4 edited

Legend:

Unmodified
Added
Removed
  • components/AddComponentDialog.tsx

    re03e6fb ra744c90  
    2020];
    2121
    22 export default function AddComponentDialog({ open, onClose, onSuccess }: {
     22export default function AddComponentDialog({ open, onClose, onSuccess, prefillData }: {
    2323    open: boolean;
    2424    onClose: () => void;
    2525    onSuccess: () => void;
     26    prefillData?: {
     27        type?: string;
     28        suggestionLink?: string;
     29        suggestionDescription?: string;
     30    };
    2631}) {
    2732    const [name, setName] = useState('');
     
    6873        }
    6974    }, [type]);
     75
     76    useEffect(() => {
     77        if (open && prefillData) {
     78            if (prefillData.type) setType(prefillData.type);
     79        }
     80    }, [open, prefillData]);
    7081
    7182    const handleFieldChange = (fieldName: string, value: any) => {
  • components/ComponentDialog.tsx

    re03e6fb ra744c90  
    4040
    4141    useEffect(() => {
     42        if (open && category) {
     43            setSuggestForm({
     44                link: '',
     45                description: '',
     46                componentType: category
     47            });
     48        }
     49    }, [open, category]);
     50
     51    useEffect(() => {
    4252        const timeoutId = setTimeout(() => {
    4353            setSearchQuery(tempSearchQuery);
     
    4656    }, [tempSearchQuery]);
    4757
    48     // Load user auth
    4958    useEffect(() => {
    5059        if (open) {
     
    97106            [e.target.name]: e.target.value
    98107        });
    99     };
     108    }
     109
     110
    100111
    101112    const submitSuggestion = async () => {
  • components/Navbar.tsx

    re03e6fb ra744c90  
    77import Menu from "@mui/material/Menu";
    88import MenuItem from "@mui/material/MenuItem";
     9import IconButton from "@mui/material/IconButton";
     10import Drawer from "@mui/material/Drawer";
     11import List from "@mui/material/List";
     12import ListItem from "@mui/material/ListItem";
     13import ListItemButton from "@mui/material/ListItemButton";
     14import ListItemIcon from "@mui/material/ListItemIcon";
     15import ListItemText from "@mui/material/ListItemText";
     16import Divider from "@mui/material/Divider";
     17import Collapse from "@mui/material/Collapse";
     18
    919import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
     20import MenuIcon from '@mui/icons-material/Menu';
     21import CloseIcon from '@mui/icons-material/Close';
     22import BuildIcon from '@mui/icons-material/Build';
     23import ViewListIcon from '@mui/icons-material/ViewList';
     24import ExpandLess from '@mui/icons-material/ExpandLess';
     25import ExpandMore from '@mui/icons-material/ExpandMore';
     26import PersonIcon from '@mui/icons-material/Person';
     27import LogoutIcon from '@mui/icons-material/Logout';
     28import LoginIcon from '@mui/icons-material/Login';
     29import PersonAddIcon from '@mui/icons-material/PersonAdd';
     30
    1031import {
    11     Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, ListItemText
     32    Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions
    1233} from "@mui/material";
    1334
     
    3960    { id: 'power_supply', label: 'Power Supplies', icon: <StorageIcon fontSize="small" /> },
    4061    { id: 'cooler', label: 'Cooling', icon: <StorageIcon fontSize="small" /> },
    41 
    42     // Peripherals / Accessories (Missing ones)
    4362    { id: 'network_adapter', label: 'Network Adapters (WiFi)', icon: <RouterIcon fontSize="small" /> },
    4463    { id: 'network_card', label: 'Network Cards (Ethernet)', icon: <LanIcon fontSize="small" /> },
     
    4968];
    5069
    51 
    5270export default function Navbar() {
    5371    const [auth, setAuth] = useState<AuthState | null>(null);
    5472    const [openLogoutDialog, setOpenLogoutDialog] = useState(false);
     73    const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
     74    const [mobileComponentsOpen, setMobileComponentsOpen] = useState(false);
    5575
    5676    const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
     
    7292        setBrowserOpen(true);
    7393        handleMenuClose();
     94        setMobileDrawerOpen(false);
     95        setMobileComponentsOpen(false);
    7496    };
    7597
     
    7799        e.preventDefault();
    78100        setOpenLogoutDialog(true);
     101        setMobileDrawerOpen(false);
    79102    };
    80103
     
    114137            <AppBar position="static" color="default" enableColorOnDark>
    115138                <Toolbar>
    116                     <Box sx={{ display: 'flex', alignItems: 'center', mr: 4 }}>
    117                         <Box component="img" src={LogoUrl} alt="PC Forge Logo" sx={{ height: 40, mr: 2, cursor: 'pointer' }} onClick={() => window.location.href='/'} />
    118                         <Typography variant="h6" component="a" href="/" sx={{ textDecoration: "none", color: "inherit", fontWeight: "bold" }}>
     139                    <Box sx={{ display: 'flex', alignItems: 'center', mr: { xs: 1, md: 4 } }}>
     140                        <Box
     141                            component="img"
     142                            src={LogoUrl}
     143                            alt="PC Forge Logo"
     144                            sx={{ height: 40, mr: { xs: 1, md: 2 }, cursor: 'pointer' }}
     145                            onClick={() => window.location.href='/'}
     146                        />
     147                        <Typography
     148                            variant="h6"
     149                            component="a"
     150                            href="/"
     151                            sx={{
     152                                textDecoration: "none",
     153                                color: "inherit",
     154                                fontWeight: "bold",
     155                                display: { xs: 'none', sm: 'block' }
     156                            }}
     157                        >
    119158                            PC Forge
    120159                        </Typography>
     
    140179                            {COMPONENT_CATEGORIES.map((cat) => (
    141180                                <MenuItem key={cat.id} onClick={() => handleCategorySelect(cat.id)}>
    142                                     {/*<ListItemIcon>{cat.icon}</ListItemIcon>*/}
     181                                    <ListItemIcon>{cat.icon}</ListItemIcon>
    143182                                    <ListItemText>{cat.label}</ListItemText>
    144183                                </MenuItem>
     
    151190                    <Box sx={{ flexGrow: 1 }} />
    152191
    153                     <Box sx={{ display: 'flex', gap: 1 }}>
     192                    <Box sx={{ display: { xs: 'none', md: 'flex' }, gap: 1 }}>
    154193                        {auth?.isLoggedIn ? (
    155194                            <>
     
    159198                        ) : (
    160199                            <>
    161                                 <Button color="inherit" href="/auth/login">Login</Button>
    162                                 <Button color="inherit" href="/auth/register">Register</Button>
     200                                <Button color="inherit" href="/auth/login" sx={onHoverNav}>Login</Button>
     201                                <Button color="inherit" href="/auth/register" sx={onHoverNav}>Register</Button>
    163202                            </>
    164203                        )}
    165204                    </Box>
     205
     206                    <IconButton
     207                        color="inherit"
     208                        edge="end"
     209                        onClick={() => setMobileDrawerOpen(true)}
     210                        sx={{ display: { xs: 'block', md: 'none' } }}
     211                    >
     212                        <MenuIcon />
     213                    </IconButton>
    166214                </Toolbar>
    167215            </AppBar>
     216
     217            <Drawer
     218                anchor="right"
     219                open={mobileDrawerOpen}
     220                onClose={() => setMobileDrawerOpen(false)}
     221                sx={{
     222                    display: { xs: 'block', md: 'none' },
     223                    '& .MuiDrawer-paper': { width: 280 , height: '60%'}
     224                }}
     225            >
     226                <Box sx={{ p: 2, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
     227                    <Typography variant="h6" fontWeight="bold">Menu</Typography>
     228                    <IconButton onClick={() => setMobileDrawerOpen(false)}>
     229                        <CloseIcon />
     230                    </IconButton>
     231                </Box>
     232                <Divider />
     233
     234                <List>
     235                    <ListItem disablePadding>
     236                        <ListItemButton onClick={() => { window.location.href = '/forge'; }}>
     237                            <ListItemIcon><BuildIcon /></ListItemIcon>
     238                            <ListItemText primary="Forge" />
     239                        </ListItemButton>
     240                    </ListItem>
     241
     242                    <ListItem disablePadding>
     243                        <ListItemButton onClick={() => setMobileComponentsOpen(!mobileComponentsOpen)}>
     244                            <ListItemIcon><ViewListIcon /></ListItemIcon>
     245                            <ListItemText primary="Components" />
     246                            {mobileComponentsOpen ? <ExpandLess /> : <ExpandMore />}
     247                        </ListItemButton>
     248                    </ListItem>
     249                    <Collapse in={mobileComponentsOpen} timeout="auto" unmountOnExit>
     250                        <List component="div" disablePadding>
     251                            {COMPONENT_CATEGORIES.map((cat) => (
     252                                <ListItemButton
     253                                    key={cat.id}
     254                                    sx={{ pl: 4 }}
     255                                    onClick={() => handleCategorySelect(cat.id)}
     256                                >
     257                                    <ListItemIcon>{cat.icon}</ListItemIcon>
     258                                    <ListItemText primary={cat.label} />
     259                                </ListItemButton>
     260                            ))}
     261                        </List>
     262                    </Collapse>
     263
     264                    <ListItem disablePadding>
     265                        <ListItemButton onClick={() => { window.location.href = '/completed-builds'; }}>
     266                            <ListItemIcon><ViewListIcon /></ListItemIcon>
     267                            <ListItemText primary="Completed Builds" />
     268                        </ListItemButton>
     269                    </ListItem>
     270
     271                    <Divider sx={{ my: 1 }} />
     272
     273                    {auth?.isLoggedIn ? (
     274                        <>
     275                            <ListItem disablePadding>
     276                                <ListItemButton onClick={() => { window.location.href = checkDashboardUrl; }}>
     277                                    <ListItemIcon><PersonIcon /></ListItemIcon>
     278                                    <ListItemText primary={"My Profile"} />
     279                                </ListItemButton>
     280                            </ListItem>
     281                            <ListItem disablePadding>
     282                                <ListItemButton onClick={handleLogoutClick}>
     283                                    <ListItemIcon><LogoutIcon /></ListItemIcon>
     284                                    <ListItemText primary="Logout" />
     285                                </ListItemButton>
     286                            </ListItem>
     287                        </>
     288                    ) : (
     289                        <>
     290                            <ListItem disablePadding>
     291                                <ListItemButton onClick={() => { window.location.href = '/auth/login'; }}>
     292                                    <ListItemIcon><LoginIcon /></ListItemIcon>
     293                                    <ListItemText primary="Login" />
     294                                </ListItemButton>
     295                            </ListItem>
     296                            <ListItem disablePadding>
     297                                <ListItemButton onClick={() => { window.location.href = '/auth/register'; }}>
     298                                    <ListItemIcon><PersonAddIcon /></ListItemIcon>
     299                                    <ListItemText primary="Register" />
     300                                </ListItemButton>
     301                            </ListItem>
     302                        </>
     303                    )}
     304                </List>
     305            </Drawer>
    168306
    169307            <Dialog open={openLogoutDialog} onClose={() => setOpenLogoutDialog(false)}>
  • pages/dashboard/admin/+Page.tsx

    re03e6fb ra744c90  
    5151    const [adminComment, setAdminComment] = useState("");
    5252
     53    const [createComponentDialog, setCreateComponentDialog] = useState<{
     54        open: boolean;
     55        suggestion: any | null;
     56    }>({open: false, suggestion: null});
     57
    5358    const loadData = () => {
    5459        setLoading(true);
     
    143148    if (loading) {
    144149        return (
    145             <Container maxWidth="xl" sx={{mt: 4, mb: 10, display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '50vh'}}>
    146                 <CircularProgress />
     150            <Container maxWidth="xl" sx={{
     151                mt: 4,
     152                mb: 10,
     153                display: 'flex',
     154                justifyContent: 'center',
     155                alignItems: 'center',
     156                minHeight: '50vh'
     157            }}>
     158                <CircularProgress/>
    147159            </Container>
    148160        );
     
    196208                                size="large"
    197209                                fullWidth
    198                                 startIcon={<BuildIcon />}
     210                                startIcon={<BuildIcon/>}
    199211                                onClick={() => setAddDialogOpen(true)}
    200                                 sx={{ mb: 2 }}
     212                                sx={{mb: 2}}
    201213                            >
    202214                                Add Component
     
    213225                                    <MemoryIcon color="secondary" sx={{mr: 1}}/>
    214226                                    <Typography variant="h6" fontWeight="bold">
    215                                         Suggested Components ({data.componentSuggestions?.length || 0})
     227                                        Component Suggestions ({data.componentSuggestions?.length || 0})
    216228                                    </Typography>
    217229                                </Box>
    218230
    219231                                {data.componentSuggestions?.length === 0 ? (
    220                                     <Typography color="text.secondary">No pending suggestions.</Typography>
     232                                    <Typography color="text.secondary">No suggestions.</Typography>
    221233                                ) : (
    222234                                    <Table size="small">
     
    226238                                                <TableCell>Type</TableCell>
    227239                                                <TableCell>User</TableCell>
     240                                                <TableCell>Status</TableCell>
    228241                                                <TableCell align="right">Actions</TableCell>
    229242                                            </TableRow>
     
    245258                                                    <TableCell>{sug.componentType?.toUpperCase()}</TableCell>
    246259                                                    <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>
    247271                                                    <TableCell align="right">
    248                                                         <IconButton
    249                                                             size="small"
    250                                                             color="success"
    251                                                             onClick={() => openSuggestionReview(sug.id, 'approved')}
    252                                                         >
    253                                                             <CheckCircleIcon/>
    254                                                         </IconButton>
    255                                                         <IconButton
    256                                                             size="small"
    257                                                             color="error"
    258                                                             onClick={() => openSuggestionReview(sug.id, 'rejected')}
    259                                                         >
    260                                                             <CancelIcon/>
    261                                                         </IconButton>
     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                                                        )}
    262307                                                    </TableCell>
    263308                                                </TableRow>
     
    299344                                                            color="warning"
    300345                                                            size="small"
    301                                                             startIcon={<BuildIcon />}
     346                                                            startIcon={<BuildIcon/>}
    302347                                                            onClick={(e) => {
    303348                                                                e.stopPropagation();
     
    337382                                                            color="error"
    338383                                                            size="small"
    339                                                             startIcon={<DeleteIcon />}
     384                                                            startIcon={<DeleteIcon/>}
    340385                                                            onClick={(e) => {
    341386                                                                e.stopPropagation();
     
    358403            </Grid>
    359404
    360             <Dialog open={suggestionDialog.open} onClose={() => setSuggestionDialog({...suggestionDialog, open: false})}>
     405            <Dialog open={suggestionDialog.open}
     406                    onClose={() => setSuggestionDialog({...suggestionDialog, open: false})}>
    361407                <DialogTitle>Review Component Suggestion</DialogTitle>
    362408                <DialogContent>
     
    369415                        value={adminComment}
    370416                        onChange={(e) => setAdminComment(e.target.value)}
    371                         sx={{ mt: 2 }}
     417                        sx={{mt: 2}}
    372418                    />
    373419                </DialogContent>
     
    448494                        color="error"
    449495                        disabled={deleteLoading}
    450                         startIcon={deleteLoading ? <CircularProgress size={20}/> : <DeleteIcon />}
     496                        startIcon={deleteLoading ? <CircularProgress size={20}/> : <DeleteIcon/>}
    451497                    >
    452498                        {deleteLoading ? 'Deleting...' : 'Delete Build'}
     
    471517                }}
    472518            />
     519
     520            <AddComponentDialog
     521                open={createComponentDialog.open}
     522                onClose={() => setCreateComponentDialog({ open: false, suggestion: null })}
     523                onSuccess={() => {
     524                    loadData();
     525                    setCreateComponentDialog({ open: false, suggestion: null });
     526                }}
     527                prefillData={createComponentDialog.suggestion ? {
     528                    type: createComponentDialog.suggestion.componentType,
     529                    suggestionLink: createComponentDialog.suggestion.link,
     530                    suggestionDescription: createComponentDialog.suggestion.description
     531                } : undefined}
     532            />
    473533        </Container>
    474534    );
Note: See TracChangeset for help on using the changeset viewer.