source: components/ComponentDialog.tsx@ 107303c

main
Last change on this file since 107303c was 6ce6739, checked in by Mihail <mihail2.naumov@…>, 7 months ago

Added real images for components

  • Property mode set to 100644
File size: 11.2 KB
Line 
1import React, {useEffect, useState} from 'react';
2import {
3 Dialog, DialogContent, IconButton, Grid, Box, Typography,
4 Slider, FormControl, InputLabel, Select, MenuItem, Checkbox, ListItemText,
5 Card, CardContent, CardMedia, Button, CircularProgress, Divider, AppBar, Toolbar
6} from '@mui/material';
7import CloseIcon from '@mui/icons-material/Close';
8import FilterListIcon from '@mui/icons-material/FilterList';
9import SortIcon from '@mui/icons-material/Sort';
10
11import {onGetAllComponents} from '../pages/+Layout.telefunc';
12import ComponentDetailsDialog from "./ComponentDetailsDialog";
13
14const formatMoney = (amount: number) => `$${amount}`;
15
16export default function ComponentBrowserDialog({open, category, onClose}: any) {
17 const [components, setComponents] = useState<any[]>([]);
18 const [loading, setLoading] = useState(false);
19
20 const [selectedComponent, setSelectedComponent] = useState<any>(null);
21 const [priceRange, setPriceRange] = useState<number[]>([0, 2000]);
22 const [selectedBrands, setSelectedBrands] = useState<string[]>([]);
23 const [availableBrands, setAvailableBrands] = useState<string[]>([]);
24
25 const [sortOrder, setSortOrder] = useState<string>('default');
26
27 useEffect(() => {
28 if (open && category) {
29 setLoading(true);
30 setSortOrder('price_desc');
31
32 onGetAllComponents({componentType: category})
33 .then((data) => {
34 setComponents(data);
35
36 const brands = Array.from(new Set(data.map((c: any) => c.brand)));
37 setAvailableBrands(brands as string[]);
38
39 const maxPrice = data.length > 0 ? Math.max(...data.map((c: any) => Number(c.price))) : 2000;
40 setPriceRange([0, Math.ceil(maxPrice)]);
41 })
42 .catch(console.error)
43 .finally(() => setLoading(false));
44 }
45 }, [open, category]);
46
47 let processedComponents = components.filter(comp => {
48 const matchesBrand = selectedBrands.length === 0 || selectedBrands.includes(comp.brand);
49 const matchesPrice = Number(comp.price) >= priceRange[0] && Number(comp.price) <= priceRange[1];
50 return matchesBrand && matchesPrice;
51 });
52
53 if (sortOrder === 'price_asc') {
54 processedComponents.sort((a, b) => Number(a.price) - Number(b.price));
55 } else if (sortOrder === 'price_desc') {
56 processedComponents.sort((a, b) => Number(b.price) - Number(a.price));
57 }
58
59 const handleBrandChange = (event: any) => {
60 const value = event.target.value;
61 setSelectedBrands(typeof value === 'string' ? value.split(',') : value);
62 };
63
64 if (!open) return null;
65
66 return (
67 <Dialog
68 open={open}
69 onClose={onClose}
70 maxWidth="xl"
71 fullWidth
72 sx={{height: '90vh'}}
73 >
74 <AppBar position="relative" sx={{bgcolor: '#ff8201'}}>
75 <Toolbar>
76 <Typography sx={{ml: 2, flex: 1, textTransform: 'capitalize'}} variant="h6" component="div">
77 Browsing: <b>{category === 'gpu' ? 'Graphics Cards' : category?.toUpperCase()}</b>
78 </Typography>
79 <IconButton edge="start" color="inherit" onClick={onClose}>
80 <CloseIcon/>
81 </IconButton>
82 </Toolbar>
83 </AppBar>
84
85 <DialogContent sx={{p: 0, display: 'flex', height: '100%'}}>
86 <Box sx={{
87 width: 300,
88 borderRight: '1px solid #ddd',
89 p: 3,
90 bgcolor: '#1e1e1e',
91 display: {xs: 'none', md: 'block'}
92 }}>
93 <Box sx={{display: 'flex', alignItems: 'center', mb: 2}}>
94 <SortIcon sx={{mr: 1}}/>
95 <Typography variant="h6">Sort By</Typography>
96 </Box>
97 <FormControl fullWidth size="small" sx={{mb: 4}}>
98 <InputLabel>Price</InputLabel>
99 <Select
100 value={sortOrder}
101 label="Price"
102 onChange={(e) => setSortOrder(e.target.value)}>
103 {/*<MenuItem value="default">Featured</MenuItem>*/}
104 <MenuItem value="price_asc">Price: Low to High</MenuItem>
105 <MenuItem value="price_desc">Price: High to Low</MenuItem>
106 </Select>
107 </FormControl>
108
109 <Divider sx={{mb: 3}}/>
110
111 <Box sx={{display: 'flex', alignItems: 'center', mb: 2}}>
112 <FilterListIcon sx={{mr: 1}}/>
113 <Typography variant="h6">Filters</Typography>
114 </Box>
115
116 <Typography gutterBottom fontWeight="bold">Price Range</Typography>
117 <Slider
118 value={priceRange}
119 onChange={(_, newValue) => setPriceRange(newValue as number[])}
120 valueLabelDisplay="auto"
121 min={0}
122 max={components.length > 0 ? Math.max(...components.map(c => Number(c.price))) : 2000}
123 sx={{color: '#ff8201', mb: 2}}
124 />
125 <Box sx={{display: 'flex', justifyContent: 'space-between', mb: 2}}>
126 <Typography variant="caption">{formatMoney(priceRange[0])}</Typography>
127 <Typography variant="caption">{formatMoney(priceRange[1])}</Typography>
128 </Box>
129
130 <FormControl fullWidth size="small">
131 <InputLabel>Brands</InputLabel>
132 <Select
133 multiple
134 value={selectedBrands}
135 onChange={handleBrandChange}
136 renderValue={(selected) => selected.join(', ')}
137 label="Brands"
138 >
139 {availableBrands.map((brand) => (
140 <MenuItem key={brand} value={brand}>
141 <Checkbox checked={selectedBrands.indexOf(brand) > -1}/>
142 <ListItemText primary={brand}/>
143 </MenuItem>
144 ))}
145 </Select>
146 </FormControl>
147 </Box>
148
149 <Box sx={{flex: 1, p: 3, overflowY: 'auto'}}>
150 {loading ? (
151 <Box sx={{display: 'flex', justifyContent: 'center', mt: 10}}>
152 <CircularProgress sx={{color: '#ff8201'}}/>
153 </Box>
154 ) : (
155 <Grid container spacing={1}>
156 {processedComponents.map((comp) => (
157 <Grid item xs={12} sm={6} md={4} lg={3} key={comp.id}>
158 <Card elevation={1} sx={{
159 height: '100%',
160 display: 'flex',
161 flexDirection: 'column',
162 maxWidth: 220,
163 width: 220,
164 overflow: 'hidden',
165 whiteSpace: 'nowrap',
166 textOverflow: 'ellipsis'
167 }}>
168 <CardMedia
169 component="img"
170 height="140"
171 // image={`https://placehold.co/400x400?text=${encodeURIComponent(comp.name)}`}
172 image={comp.imgUrl}
173 alt={comp.name}
174 sx={{p: 2, objectFit: 'contain'}}
175 />
176 <CardContent sx={{flexGrow: 1}}>
177 <Typography variant="caption" color="text.secondary"
178 sx={{textTransform: 'uppercase'}}>
179 {comp.brand}
180 </Typography>
181 <Typography variant="subtitle1" fontWeight="bold" sx={{
182 lineHeight: 1.2,
183 mb: 1,
184 }}>
185 {comp.name}
186 </Typography>
187
188 <Box sx={{
189 display: 'flex',
190 justifyContent: 'space-between',
191 alignItems: 'center',
192 mt: 2
193 }}>
194 <Typography variant="h6" color="primary.main">
195 {formatMoney(comp.price)}
196 </Typography>
197 </Box>
198 </CardContent>
199 <Box sx={{p: 2, pt: 0}}>
200 <Button
201 fullWidth
202 variant="outlined"
203 onClick={() => setSelectedComponent(comp)}
204 sx={{
205 backgroundColor: '#ff8201',
206 color: 'white',
207 borderColor: '#ff8201',
208 onHover: {backgroundColor: '#e67300', borderColor: '#e67300'}
209 }}
210 >
211 Details
212 </Button>
213 </Box>
214 </Card>
215 </Grid>
216 ))}
217 {processedComponents.length === 0 && (
218 <Box sx={{width: '100%', textAlign: 'center', mt: 5}}>
219 <Typography variant="h6" color="text.secondary">No components match.</Typography>
220 </Box>
221 )}
222 </Grid>
223 )}
224 </Box>
225 </DialogContent>
226 <ComponentDetailsDialog
227 open={!!selectedComponent}
228 component={selectedComponent}
229 onClose={() => setSelectedComponent(null)}
230 />
231 </Dialog>
232 );
233}
Note: See TracBrowser for help on using the repository browser.