Changeset e599341 for components
- Timestamp:
- 12/28/25 00:07:22 (7 months ago)
- Branches:
- main
- Children:
- 9c87509
- Parents:
- f7c0b0d
- Location:
- components
- Files:
-
- 3 edited
-
BuildCard.tsx (modified) (3 diffs)
-
BuildDetailsDialog.tsx (modified) (6 diffs)
-
Navbar.tsx (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
components/BuildCard.tsx
rf7c0b0d re599341 2 2 import { Card, CardMedia, CardContent, Typography, Box, Chip } from '@mui/material'; 3 3 import StarIcon from '@mui/icons-material/Star'; 4 import PersonIcon from '@mui/icons-material/Person';4 // import PersonIcon from '@mui/icons-material/Person'; 5 5 6 6 export default function BuildCard({ build, onClick }: { build: any, onClick: () => void }) { 7 const formattedPrice = new Intl.NumberFormat('en-US', { style: 'currency', currency: ' USD' }).format(build.total_price || 0);7 const formattedPrice = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'EUR' }).format(build.total_price || 0); 8 8 9 9 return ( … … 25 25 {/*Ne se renderira user-ot*/} 26 26 <Box sx={{ display: 'flex', alignItems: 'center', mb: 1, color: 'text.secondary' }}> 27 {/* <PersonIcon sx={{ fontSize: 16, mr: 0.5 }} />*/} 28 {/* <Typography variant="caption">{build.user || 'Unknown'}</Typography>*/} 29 <Typography variant="h6" gutterBottom noWrap title={build.cpu}>{build.cpu}</Typography> 30 <Typography variant="h6" gutterBottom noWrap title={build.gpu}>{build.gpu}</Typography> 27 {/* <PersonIcon sx={{ fontSize: 16, mr: 0.5 }} />*/} 28 {/* <Typography variant="caption">{build.user || 'Unknown'}</Typography>*/} 31 29 </Box> 32 30 … … 36 34 <StarIcon fontSize="small" sx={{ color: '#faaf00', mr: 0.5 }} /> 37 35 <Typography variant="body2" fontWeight="bold"> 38 {Number(build.avg _rating || 5).toFixed(1)}36 {Number(build.avgRating || 5).toFixed(1)} 39 37 </Typography> 40 38 </Box> -
components/BuildDetailsDialog.tsx
rf7c0b0d re599341 2 2 import { 3 3 Dialog, DialogTitle, DialogContent, Button, Grid, Box, Typography, 4 IconButton, Tab, Tabs, Table, TableBody, TableCell, TableRow, Rating, TextField, Avatar, Divider, Chip, CardMedia4 IconButton, Tab, Tabs, Table, TableBody, TableCell, TableRow, Rating, TextField, Avatar, Chip 5 5 } from '@mui/material'; 6 6 import CloseIcon from '@mui/icons-material/Close'; … … 8 8 import FavoriteIcon from '@mui/icons-material/Favorite'; 9 9 import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder'; 10 import PersonIcon from "@mui/icons-material/Person"; 10 11 import { onGetBuildDetails, onSetReview, onToggleFavorite } from '../pages/+Layout.telefunc'; 11 import PersonIcon from "@mui/icons-material/Person";12 12 13 13 const formatPrice = (price: any) => new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(Number(price) || 0); … … 24 24 if (open && buildId) { 25 25 setLoading(true); 26 setReviewText(""); 27 setRatingVal(5); 28 26 29 onGetBuildDetails({ buildId }) 27 30 .then(data => { … … 43 46 if (!currentUser) return alert("Please login to review."); 44 47 await onSetReview({ buildId, content: reviewText }); 48 49 setReviewText(""); 45 50 46 51 const refreshed = await onGetBuildDetails({ buildId }); … … 81 86 <Box sx={{ p: 3 }}> 82 87 {tabIndex === 0 && ( 83 <Grid container spacing={ 4}>88 <Grid container spacing={2}> 84 89 <Grid item xs={12} md={8}> 85 90 <Table size="small"> … … 88 93 <TableRow key={comp.id}> 89 94 <TableCell sx={{ width: 50 }}> 90 {/*<Avatar*/} 91 {/* src={comp.img_url || undefined}*/} 92 {/* variant="rounded"*/} 93 {/* sx={{ width: 40, height: 40, bgcolor: '#ff8201' }}*/} 94 {/*>*/} 95 {/* {comp.type?.[0]?.toUpperCase()}*/} 96 {/*</Avatar>*/} 97 {/*<PersonIcon sx={{ fontSize: 16 }} />*/} 98 <CardMedia 99 component="img" 100 height="50" 101 width="50" 102 image={"https://placehold.co/400x400?text=CPU"} 103 104 /> 95 <Avatar 96 src={comp.img_url || undefined} 97 variant="rounded" 98 sx={{ width: 45, height: 45, bgcolor: '#ff8201'}} 99 > 100 {comp.type?.substring(0, 3)?.toUpperCase()} 101 </Avatar> 105 102 </TableCell> 106 103 <TableCell> -
components/Navbar.tsx
rf7c0b0d re599341 4 4 import Typography from "@mui/material/Typography"; 5 5 import Button from "@mui/material/Button"; 6 import InputBase from "@mui/material/InputBase";7 6 import Box from "@mui/material/Box"; 8 import { styled } from "@mui/material/styles"; 7 import Menu from "@mui/material/Menu"; 8 import MenuItem from "@mui/material/MenuItem"; 9 import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; 10 import { 11 Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, ListItemText 12 } from "@mui/material"; 13 14 import MemoryIcon from '@mui/icons-material/Memory'; 15 import DeveloperBoardIcon from '@mui/icons-material/DeveloperBoard'; 16 import StorageIcon from '@mui/icons-material/Storage'; 17 import SdStorageIcon from '@mui/icons-material/SdStorage'; 18 import RouterIcon from '@mui/icons-material/Router'; 19 import LanIcon from '@mui/icons-material/Lan'; 20 import SpeakerIcon from '@mui/icons-material/Speaker'; 21 import AlbumIcon from '@mui/icons-material/Album'; 22 import SdCardIcon from '@mui/icons-material/SdCard'; 23 import CableIcon from '@mui/icons-material/Cable'; 24 9 25 import LogoUrl from '../assets/projectlogo.png'; 10 import { onGetAuthState} from "../pages/+Layout.telefunc";26 import { onGetAuthState } from "../pages/+Layout.telefunc"; 11 27 28 import ComponentDialog from "./ComponentDialog"; 12 29 13 type AuthState = { isLoggedIn: boolean; username: string | null }; 30 type AuthState = { isLoggedIn: boolean; username: string | null; isAdmin?: boolean }; 31 32 const COMPONENT_CATEGORIES = [ 33 { id: 'cpu', label: 'Processors', icon: <MemoryIcon fontSize="small" /> }, 34 { id: 'gpu', label: 'Graphics Cards', icon: <DeveloperBoardIcon fontSize="small" /> }, 35 { id: 'motherboard', label: 'Motherboards', icon: <DeveloperBoardIcon fontSize="small" /> }, 36 { id: 'memory', label: 'Memory (RAM)', icon: <SdStorageIcon fontSize="small" /> }, 37 { id: 'storage', label: 'Storage', icon: <StorageIcon fontSize="small" /> }, 38 { id: 'case', label: 'Cases', icon: <StorageIcon fontSize="small" /> }, 39 { id: 'power_supply', label: 'Power Supplies', icon: <StorageIcon fontSize="small" /> }, 40 { id: 'cooler', label: 'Cooling', icon: <StorageIcon fontSize="small" /> }, 41 42 // Peripherals / Accessories (Missing ones) 43 { id: 'network_adapter', label: 'Network Adapters (WiFi)', icon: <RouterIcon fontSize="small" /> }, 44 { id: 'network_card', label: 'Network Cards (Ethernet)', icon: <LanIcon fontSize="small" /> }, 45 { id: 'sound_card', label: 'Sound Cards', icon: <SpeakerIcon fontSize="small" /> }, 46 { id: 'optical_drive', label: 'Optical Drives', icon: <AlbumIcon fontSize="small" /> }, 47 { id: 'memory_card', label: 'Memory Cards', icon: <SdCardIcon fontSize="small" /> }, 48 { id: 'cables', label: 'Cables', icon: <CableIcon fontSize="small" /> }, 49 ]; 50 14 51 15 52 export default function Navbar() { 16 53 const [auth, setAuth] = useState<AuthState | null>(null); 54 const [openLogoutDialog, setOpenLogoutDialog] = useState(false); 17 55 18 const handleLogout = async (e: React.MouseEvent) => { // funkcija za logout da go resetira auth state, za da ne go pise username-ot u navbar, plus te vrakja na login nazad 56 const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); 57 const openMenu = Boolean(anchorEl); 58 59 const [browserOpen, setBrowserOpen] = useState(false); 60 const [selectedCategory, setSelectedCategory] = useState<string | null>(null); 61 62 const handleComponentsClick = (event: React.MouseEvent<HTMLButtonElement>) => { 63 setAnchorEl(event.currentTarget); 64 }; 65 66 const handleMenuClose = () => { 67 setAnchorEl(null); 68 }; 69 70 const handleCategorySelect = (categoryId: string) => { 71 setSelectedCategory(categoryId); 72 setBrowserOpen(true); 73 handleMenuClose(); 74 }; 75 76 const handleLogoutClick = (e: React.MouseEvent) => { 19 77 e.preventDefault(); 78 setOpenLogoutDialog(true); 79 }; 20 80 21 setAuth({ isLoggedIn: false, username: null }); 22 window.location.href = "/api/auth/signout?callbackUrl=/auth/login"; 81 const confirmLogout = async () => { 82 setAuth({ isLoggedIn: false, username: null, isAdmin: false }); 83 setOpenLogoutDialog(false); 84 const csrfRes = await fetch("/api/auth/csrf"); 85 const { csrfToken } = await csrfRes.json(); 86 await fetch("/api/auth/signout", { 87 method: "POST", 88 headers: { "Content-Type": "application/x-www-form-urlencoded" }, 89 body: new URLSearchParams({ csrfToken: csrfToken, callbackUrl: "/" }), 90 }); 91 window.location.href = "/"; 23 92 }; 24 93 … … 26 95 let active = true; 27 96 onGetAuthState() 28 .then((data) => active && setAuth({ isLoggedIn: data.isLoggedIn, username: data.username })) 29 .catch(() => active && setAuth({ isLoggedIn: false, username: null })); 97 .then((data) => active && setAuth({ 98 isLoggedIn: data.isLoggedIn, 99 username: data.username, 100 isAdmin: data.isAdmin 101 })) 102 .catch(() => active && setAuth({ isLoggedIn: false, username: null, isAdmin: false })); 30 103 return () => { active = false; }; 31 104 }, []); 32 105 106 const checkDashboardUrl = auth?.isAdmin ? '/dashboard/admin' : '/dashboard/user'; 33 107 const onHoverNav = { 34 108 color: 'inherit', 35 '&:hover': { 36 backgroundColor: '#ff8201', 37 color: 'white', 38 fontWeight: 'bold', 39 } 109 '&:hover': { backgroundColor: '#ff8201', color: 'white', fontWeight: 'bold' } 40 110 }; 41 111 42 112 return ( 43 <AppBar position="static" color="default" enableColorOnDark> 44 <Toolbar> 45 <Box sx={{ display: 'flex', alignItems: 'center', mr: 4 }}> 46 <Box 47 component="img" 48 src={LogoUrl} 49 alt="PC Forge Logo" 50 sx={{ height: 40, mr: 2, cursor: 'pointer' }} 51 /> 52 <Typography 53 variant="h6" 54 component="a" 55 href="/" 56 sx={{ textDecoration: "none", color: "inherit", fontWeight: "bold" }} 57 > 58 PC Forge 59 </Typography> 60 </Box> 113 <> 114 <AppBar position="static" color="default" enableColorOnDark> 115 <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" }}> 119 PC Forge 120 </Typography> 121 </Box> 61 122 62 <Box sx={{ display: { xs: "none", md: "flex" }, gap: 2}}> 63 <Button color="inherit" href="/forge" sx={onHoverNav}>Forge</Button> 64 <Button color="inherit" href="/components" sx={onHoverNav}>Components</Button> 65 <Button color="inherit" href="/completed-builds" sx={onHoverNav}>Completed Builds</Button> 66 </Box> 123 <Box sx={{ display: { xs: "none", md: "flex" }, gap: 2 }}> 124 <Button color="inherit" href="/forge" sx={onHoverNav}>Forge</Button> 67 125 68 <Box sx={{ flexGrow: 1 }} /> 126 <Button 127 color="inherit" 128 onClick={handleComponentsClick} 129 endIcon={<KeyboardArrowDownIcon />} 130 sx={onHoverNav} 131 > 132 Components 133 </Button> 134 <Menu 135 anchorEl={anchorEl} 136 open={openMenu} 137 onClose={handleMenuClose} 138 MenuListProps={{ 'aria-labelledby': 'basic-button' }} 139 > 140 {COMPONENT_CATEGORIES.map((cat) => ( 141 <MenuItem key={cat.id} onClick={() => handleCategorySelect(cat.id)}> 142 {/*<ListItemIcon>{cat.icon}</ListItemIcon>*/} 143 <ListItemText>{cat.label}</ListItemText> 144 </MenuItem> 145 ))} 146 </Menu> 69 147 70 <Box sx={{ display: 'flex', gap: 1 }}> 71 {auth?.isLoggedIn ? ( 72 <> 73 <Button sx={onHoverNav} color="inherit" href="/dashboard/user">{auth.username}</Button> 74 <Button sx={onHoverNav} color="inherit" onClick={handleLogout}>Logout</Button> 75 </> 76 ) : ( 77 <> 78 <Button color="inherit" href="/auth/login">Login</Button> 79 <Button color="inherit" href="/auth/register">Register</Button> 80 </> 81 )} 82 </Box> 83 </Toolbar> 84 </AppBar> 148 <Button color="inherit" href="/completed-builds" sx={onHoverNav}>Completed Builds</Button> 149 </Box> 150 151 <Box sx={{ flexGrow: 1 }} /> 152 153 <Box sx={{ display: 'flex', gap: 1 }}> 154 {auth?.isLoggedIn ? ( 155 <> 156 <Button sx={onHoverNav} color="inherit" href={checkDashboardUrl}>{auth.username}</Button> 157 <Button sx={onHoverNav} color="inherit" onClick={handleLogoutClick}>Logout</Button> 158 </> 159 ) : ( 160 <> 161 <Button color="inherit" href="/auth/login">Login</Button> 162 <Button color="inherit" href="/auth/register">Register</Button> 163 </> 164 )} 165 </Box> 166 </Toolbar> 167 </AppBar> 168 169 <Dialog open={openLogoutDialog} onClose={() => setOpenLogoutDialog(false)}> 170 <DialogTitle>Confirm Logout</DialogTitle> 171 <DialogContent> 172 <DialogContentText>Are you sure you want to leave the Forge?</DialogContentText> 173 </DialogContent> 174 <DialogActions> 175 <Button onClick={() => setOpenLogoutDialog(false)}>Cancel</Button> 176 <Button onClick={confirmLogout} color="error" variant="contained" autoFocus>Logout</Button> 177 </DialogActions> 178 </Dialog> 179 180 <ComponentDialog 181 open={browserOpen} 182 category={selectedCategory} 183 onClose={() => setBrowserOpen(false)} 184 /> 185 </> 85 186 ); 86 187 }
Note:
See TracChangeset
for help on using the changeset viewer.
