Changeset a744c90
- Timestamp:
- 01/28/26 12:13:13 (5 months ago)
- 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. - Files:
-
- 4 edited
-
components/AddComponentDialog.tsx (modified) (2 diffs)
-
components/ComponentDialog.tsx (modified) (3 diffs)
-
components/Navbar.tsx (modified) (9 diffs)
-
pages/dashboard/admin/+Page.tsx (modified) (12 diffs)
Legend:
- Unmodified
- Added
- Removed
-
components/AddComponentDialog.tsx
re03e6fb ra744c90 20 20 ]; 21 21 22 export default function AddComponentDialog({ open, onClose, onSuccess }: {22 export default function AddComponentDialog({ open, onClose, onSuccess, prefillData }: { 23 23 open: boolean; 24 24 onClose: () => void; 25 25 onSuccess: () => void; 26 prefillData?: { 27 type?: string; 28 suggestionLink?: string; 29 suggestionDescription?: string; 30 }; 26 31 }) { 27 32 const [name, setName] = useState(''); … … 68 73 } 69 74 }, [type]); 75 76 useEffect(() => { 77 if (open && prefillData) { 78 if (prefillData.type) setType(prefillData.type); 79 } 80 }, [open, prefillData]); 70 81 71 82 const handleFieldChange = (fieldName: string, value: any) => { -
components/ComponentDialog.tsx
re03e6fb ra744c90 40 40 41 41 useEffect(() => { 42 if (open && category) { 43 setSuggestForm({ 44 link: '', 45 description: '', 46 componentType: category 47 }); 48 } 49 }, [open, category]); 50 51 useEffect(() => { 42 52 const timeoutId = setTimeout(() => { 43 53 setSearchQuery(tempSearchQuery); … … 46 56 }, [tempSearchQuery]); 47 57 48 // Load user auth49 58 useEffect(() => { 50 59 if (open) { … … 97 106 [e.target.name]: e.target.value 98 107 }); 99 }; 108 } 109 110 100 111 101 112 const submitSuggestion = async () => { -
components/Navbar.tsx
re03e6fb ra744c90 7 7 import Menu from "@mui/material/Menu"; 8 8 import MenuItem from "@mui/material/MenuItem"; 9 import IconButton from "@mui/material/IconButton"; 10 import Drawer from "@mui/material/Drawer"; 11 import List from "@mui/material/List"; 12 import ListItem from "@mui/material/ListItem"; 13 import ListItemButton from "@mui/material/ListItemButton"; 14 import ListItemIcon from "@mui/material/ListItemIcon"; 15 import ListItemText from "@mui/material/ListItemText"; 16 import Divider from "@mui/material/Divider"; 17 import Collapse from "@mui/material/Collapse"; 18 9 19 import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; 20 import MenuIcon from '@mui/icons-material/Menu'; 21 import CloseIcon from '@mui/icons-material/Close'; 22 import BuildIcon from '@mui/icons-material/Build'; 23 import ViewListIcon from '@mui/icons-material/ViewList'; 24 import ExpandLess from '@mui/icons-material/ExpandLess'; 25 import ExpandMore from '@mui/icons-material/ExpandMore'; 26 import PersonIcon from '@mui/icons-material/Person'; 27 import LogoutIcon from '@mui/icons-material/Logout'; 28 import LoginIcon from '@mui/icons-material/Login'; 29 import PersonAddIcon from '@mui/icons-material/PersonAdd'; 30 10 31 import { 11 Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions , ListItemText32 Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions 12 33 } from "@mui/material"; 13 34 … … 39 60 { id: 'power_supply', label: 'Power Supplies', icon: <StorageIcon fontSize="small" /> }, 40 61 { id: 'cooler', label: 'Cooling', icon: <StorageIcon fontSize="small" /> }, 41 42 // Peripherals / Accessories (Missing ones)43 62 { id: 'network_adapter', label: 'Network Adapters (WiFi)', icon: <RouterIcon fontSize="small" /> }, 44 63 { id: 'network_card', label: 'Network Cards (Ethernet)', icon: <LanIcon fontSize="small" /> }, … … 49 68 ]; 50 69 51 52 70 export default function Navbar() { 53 71 const [auth, setAuth] = useState<AuthState | null>(null); 54 72 const [openLogoutDialog, setOpenLogoutDialog] = useState(false); 73 const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false); 74 const [mobileComponentsOpen, setMobileComponentsOpen] = useState(false); 55 75 56 76 const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); … … 72 92 setBrowserOpen(true); 73 93 handleMenuClose(); 94 setMobileDrawerOpen(false); 95 setMobileComponentsOpen(false); 74 96 }; 75 97 … … 77 99 e.preventDefault(); 78 100 setOpenLogoutDialog(true); 101 setMobileDrawerOpen(false); 79 102 }; 80 103 … … 114 137 <AppBar position="static" color="default" enableColorOnDark> 115 138 <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 > 119 158 PC Forge 120 159 </Typography> … … 140 179 {COMPONENT_CATEGORIES.map((cat) => ( 141 180 <MenuItem key={cat.id} onClick={() => handleCategorySelect(cat.id)}> 142 {/*<ListItemIcon>{cat.icon}</ListItemIcon>*/}181 <ListItemIcon>{cat.icon}</ListItemIcon> 143 182 <ListItemText>{cat.label}</ListItemText> 144 183 </MenuItem> … … 151 190 <Box sx={{ flexGrow: 1 }} /> 152 191 153 <Box sx={{ display: 'flex', gap: 1 }}>192 <Box sx={{ display: { xs: 'none', md: 'flex' }, gap: 1 }}> 154 193 {auth?.isLoggedIn ? ( 155 194 <> … … 159 198 ) : ( 160 199 <> 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> 163 202 </> 164 203 )} 165 204 </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> 166 214 </Toolbar> 167 215 </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> 168 306 169 307 <Dialog open={openLogoutDialog} onClose={() => setOpenLogoutDialog(false)}> -
pages/dashboard/admin/+Page.tsx
re03e6fb ra744c90 51 51 const [adminComment, setAdminComment] = useState(""); 52 52 53 const [createComponentDialog, setCreateComponentDialog] = useState<{ 54 open: boolean; 55 suggestion: any | null; 56 }>({open: false, suggestion: null}); 57 53 58 const loadData = () => { 54 59 setLoading(true); … … 143 148 if (loading) { 144 149 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/> 147 159 </Container> 148 160 ); … … 196 208 size="large" 197 209 fullWidth 198 startIcon={<BuildIcon />}210 startIcon={<BuildIcon/>} 199 211 onClick={() => setAddDialogOpen(true)} 200 sx={{ mb: 2}}212 sx={{mb: 2}} 201 213 > 202 214 Add Component … … 213 225 <MemoryIcon color="secondary" sx={{mr: 1}}/> 214 226 <Typography variant="h6" fontWeight="bold"> 215 Suggested Components ({data.componentSuggestions?.length || 0})227 Component Suggestions ({data.componentSuggestions?.length || 0}) 216 228 </Typography> 217 229 </Box> 218 230 219 231 {data.componentSuggestions?.length === 0 ? ( 220 <Typography color="text.secondary">No pendingsuggestions.</Typography>232 <Typography color="text.secondary">No suggestions.</Typography> 221 233 ) : ( 222 234 <Table size="small"> … … 226 238 <TableCell>Type</TableCell> 227 239 <TableCell>User</TableCell> 240 <TableCell>Status</TableCell> 228 241 <TableCell align="right">Actions</TableCell> 229 242 </TableRow> … … 245 258 <TableCell>{sug.componentType?.toUpperCase()}</TableCell> 246 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> 247 271 <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 )} 262 307 </TableCell> 263 308 </TableRow> … … 299 344 color="warning" 300 345 size="small" 301 startIcon={<BuildIcon />}346 startIcon={<BuildIcon/>} 302 347 onClick={(e) => { 303 348 e.stopPropagation(); … … 337 382 color="error" 338 383 size="small" 339 startIcon={<DeleteIcon />}384 startIcon={<DeleteIcon/>} 340 385 onClick={(e) => { 341 386 e.stopPropagation(); … … 358 403 </Grid> 359 404 360 <Dialog open={suggestionDialog.open} onClose={() => setSuggestionDialog({...suggestionDialog, open: false})}> 405 <Dialog open={suggestionDialog.open} 406 onClose={() => setSuggestionDialog({...suggestionDialog, open: false})}> 361 407 <DialogTitle>Review Component Suggestion</DialogTitle> 362 408 <DialogContent> … … 369 415 value={adminComment} 370 416 onChange={(e) => setAdminComment(e.target.value)} 371 sx={{ mt: 2}}417 sx={{mt: 2}} 372 418 /> 373 419 </DialogContent> … … 448 494 color="error" 449 495 disabled={deleteLoading} 450 startIcon={deleteLoading ? <CircularProgress size={20}/> : <DeleteIcon />}496 startIcon={deleteLoading ? <CircularProgress size={20}/> : <DeleteIcon/>} 451 497 > 452 498 {deleteLoading ? 'Deleting...' : 'Delete Build'} … … 471 517 }} 472 518 /> 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 /> 473 533 </Container> 474 534 );
Note:
See TracChangeset
for help on using the changeset viewer.
