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