source: components/ComponentDialog.tsx@ 3216215

main
Last change on this file since 3216215 was a932c9e, checked in by Mihail <mihail2.naumov@…>, 7 months ago

Added frontend elements 2

  • Property mode set to 100644
File size: 10.0 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 PaperProps={{ 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={{ width: 300, borderRight: '1px solid #ddd', p: 3, bgcolor: '#1e1e1e', display: { xs: 'none', md: 'block' } }}>
87 <Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
88 <SortIcon sx={{ mr: 1 }} />
89 <Typography variant="h6">Sort By</Typography>
90 </Box>
91 <FormControl fullWidth size="small" sx={{ mb: 4 }}>
92 <InputLabel>Price</InputLabel>
93 <Select
94 value={sortOrder}
95 label="Price"
96 onChange={(e) => setSortOrder(e.target.value)}>
97 {/*<MenuItem value="default">Featured</MenuItem>*/}
98 <MenuItem value="price_asc">Price: Low to High</MenuItem>
99 <MenuItem value="price_desc">Price: High to Low</MenuItem>
100 </Select>
101 </FormControl>
102
103 <Divider sx={{ mb: 3 }} />
104
105 <Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
106 <FilterListIcon sx={{ mr: 1 }} />
107 <Typography variant="h6">Filters</Typography>
108 </Box>
109
110 <Typography gutterBottom fontWeight="bold">Price Range</Typography>
111 <Slider
112 value={priceRange}
113 onChange={(_, newValue) => setPriceRange(newValue as number[])}
114 valueLabelDisplay="auto"
115 min={0}
116 max={components.length > 0 ? Math.max(...components.map(c => Number(c.price))) : 2000}
117 sx={{ color: '#ff8201', mb: 2}}
118 />
119 <Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
120 <Typography variant="caption">{formatMoney(priceRange[0])}</Typography>
121 <Typography variant="caption">{formatMoney(priceRange[1])}</Typography>
122 </Box>
123
124 <FormControl fullWidth size="small">
125 <InputLabel>Brands</InputLabel>
126 <Select
127 multiple
128 value={selectedBrands}
129 onChange={handleBrandChange}
130 renderValue={(selected) => selected.join(', ')}
131 label="Brands"
132 >
133 {availableBrands.map((brand) => (
134 <MenuItem key={brand} value={brand}>
135 <Checkbox checked={selectedBrands.indexOf(brand) > -1} />
136 <ListItemText primary={brand} />
137 </MenuItem>
138 ))}
139 </Select>
140 </FormControl>
141 </Box>
142
143 <Box sx={{ flex: 1, p: 3, overflowY: 'auto' }}>
144 {loading ? (
145 <Box sx={{ display: 'flex', justifyContent: 'center', mt: 10 }}>
146 <CircularProgress sx={{ color: '#ff8201' }} />
147 </Box>
148 ) : (
149 <Grid container spacing={3}>
150 {processedComponents.map((comp) => (
151 <Grid item xs={12} sm={6} md={4} lg={3} key={comp.id}>
152 <Card elevation={3} sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
153 <CardMedia
154 component="img"
155 height="140"
156
157 image={`https://placehold.co/400x400?text=${encodeURIComponent(comp.name)}`}
158 alt={comp.name}
159 sx={{ p: 2, objectFit: 'contain' }}
160 />
161 <CardContent sx={{ flexGrow: 1 }}>
162 <Typography variant="caption" color="text.secondary" sx={{ textTransform: 'uppercase' }}>
163 {comp.brand}
164 </Typography>
165 <Typography variant="subtitle1" fontWeight="bold" sx={{ lineHeight: 1.2, mb: 1 }}>
166 {comp.name}
167 </Typography>
168
169 <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mt: 2 }}>
170 <Typography variant="h6" color="primary.main">
171 {formatMoney(comp.price)}
172 </Typography>
173 </Box>
174 </CardContent>
175 <Box sx={{ p: 2, pt: 0}}>
176 <Button
177 fullWidth
178 variant="outlined"
179 onClick={()=> setSelectedComponent(comp)}
180 sx={{backgroundColor:'#ff8201', color: 'white', borderColor: '#ff8201', onHover: { backgroundColor: '#e67300', borderColor: '#e67300' } }}
181 >
182 Details
183 </Button>
184 </Box>
185 </Card>
186 </Grid>
187 ))}
188 {processedComponents.length === 0 && (
189 <Box sx={{ width: '100%', textAlign: 'center', mt: 5 }}>
190 <Typography variant="h6" color="text.secondary">No components match.</Typography>
191 </Box>
192 )}
193 </Grid>
194 )}
195 </Box>
196 </DialogContent>
197 <ComponentDetailsDialog
198 open={!!selectedComponent}
199 component={selectedComponent}
200 onClose={() => setSelectedComponent(null)}
201 />
202 </Dialog>
203 );
204}
Note: See TracBrowser for help on using the repository browser.