Ignore:
Timestamp:
12/29/25 00:37:49 (6 months ago)
Author:
Mihail <mihail2.naumov@…>
Branches:
main
Children:
5af32f0
Parents:
ae5d054
Message:

Added Forge Page, added suggest component, fixed styling

File:
1 edited

Legend:

Unmodified
Added
Removed
  • components/ComponentDialog.tsx

    rae5d054 r8a7f936  
    11import React, {useEffect, useState} from 'react';
    22import {
    3     Dialog, DialogContent, IconButton, Grid, Box, Typography,
     3    Dialog, DialogContent, IconButton, Box, Typography, Chip,
    44    Slider, FormControl, InputLabel, Select, MenuItem, Checkbox, ListItemText,
    5     Card, CardContent, CardMedia, Button, CircularProgress, Divider, AppBar, Toolbar
     5    Card, CardContent, CardMedia, Button, CircularProgress, AppBar, Toolbar, InputAdornment, TextField,
     6    TextField as MuiTextField, DialogTitle, DialogActions, Snackbar, Alert
    67} from '@mui/material';
    78import CloseIcon from '@mui/icons-material/Close';
    8 import FilterListIcon from '@mui/icons-material/FilterList';
    9 import SortIcon from '@mui/icons-material/Sort';
    10 
    11 import {onGetAllComponents} from '../pages/+Layout.telefunc';
     9import AddCircleIcon from '@mui/icons-material/AddCircle';
     10
     11import {onGetAllComponents, onGetAuthState, onSuggestComponent} from '../pages/+Layout.telefunc'
     12import {onGetCompatibleComponents} from '../pages/forge/forge.telefunc';
    1213import ComponentDetailsDialog from "./ComponentDetailsDialog";
    13 
    14 const formatMoney = (amount: number) => `$${amount}`;
    15 
    16 export default function ComponentBrowserDialog({open, category, onClose}: any) {
     14import SearchIcon from "@mui/icons-material/Search";
     15
     16const formatMoney = (amount: number) => `$${Number(amount).toFixed(2)}`;
     17
     18export default function ComponentDialog({open, category, onClose, mode, onSelect, currentBuildId}: any) {
    1719    const [components, setComponents] = useState<any[]>([]);
    1820    const [loading, setLoading] = useState(false);
    19 
    2021    const [selectedComponent, setSelectedComponent] = useState<any>(null);
    2122    const [priceRange, setPriceRange] = useState<number[]>([0, 2000]);
    2223    const [selectedBrands, setSelectedBrands] = useState<string[]>([]);
    2324    const [availableBrands, setAvailableBrands] = useState<string[]>([]);
    24 
    2525    const [sortOrder, setSortOrder] = useState<string>('default');
     26
     27    const [tempSearchQuery, setTempSearchQuery] = useState("");
     28    const [searchQuery, setSearchQuery] = useState("");
     29
     30    const [suggestOpen, setSuggestOpen] = useState(false);
     31    const [suggestForm, setSuggestForm] = useState({
     32        link: '',
     33        description: '',
     34        componentType: category || ''
     35    });
     36    const [suggestLoading, setSuggestLoading] = useState(false);
     37    const [snackbarOpen, setSnackbarOpen] = useState(false);
     38    const [snackbarMessage, setSnackbarMessage] = useState("");
     39    const [userId, setUserId] = useState<number | null>(null);
     40
     41    useEffect(() => {
     42        const timeoutId = setTimeout(() => {
     43            setSearchQuery(tempSearchQuery);
     44        }, 300);
     45        return () => clearTimeout(timeoutId);
     46    }, [tempSearchQuery]);
     47
     48    // Load user auth
     49    useEffect(() => {
     50        if (open) {
     51            onGetAuthState().then(userData => {
     52                setUserId(userData.userId);
     53            });
     54        }
     55    }, [open]);
    2656
    2757    useEffect(() => {
     
    3060            setSortOrder('price_desc');
    3161
    32             onGetAllComponents({componentType: category})
     62            const shouldUseCompatibility = mode === 'forge' && currentBuildId;
     63            const fetcher = shouldUseCompatibility
     64                ? onGetCompatibleComponents({
     65                    buildId: currentBuildId,
     66                    componentType: category,
     67                    limit: 100,
     68                    sort: 'price_desc'
     69                })
     70                : onGetAllComponents({
     71                    componentType: category,
     72                    limit: 100,
     73                    sort: 'price_desc'
     74                });
     75
     76            fetcher
    3377                .then((data) => {
    34                     setComponents(data);
    35 
    36                     const brands = Array.from(new Set(data.map((c: any) => c.brand)));
     78                    const comps = data || [];
     79                    setComponents(comps);
     80
     81                    const brands = Array.from(new Set(comps.map((c: any) => c.brand)));
    3782                    setAvailableBrands(brands as string[]);
    38 
    39                     const maxPrice = data.length > 0 ? Math.max(...data.map((c: any) => Number(c.price))) : 2000;
     83                    const maxPrice = comps.length > 0 ? Math.max(...comps.map((c: any) => Number(c.price))) : 2000;
    4084                    setPriceRange([0, Math.ceil(maxPrice)]);
    4185                })
    42                 .catch(console.error)
     86                .catch((err) => {
     87                    console.error('[Dialog] fetch error:', err);
     88                    setComponents([]);
     89                })
    4390                .finally(() => setLoading(false));
    4491        }
    45     }, [open, category]);
     92    }, [open, category, mode, currentBuildId]);
     93
     94    const handleSuggestChange = (e: React.ChangeEvent<HTMLInputElement>) => {
     95        setSuggestForm({
     96            ...suggestForm,
     97            [e.target.name]: e.target.value
     98        });
     99    };
     100
     101    const submitSuggestion = async () => {
     102        if (!userId) {
     103            setSnackbarMessage("Please login to submit suggestions!");
     104            setSnackbarOpen(true);
     105            return;
     106        }
     107
     108        if (!suggestForm.link || !suggestForm.description) {
     109            setSnackbarMessage("Please fill in all fields!");
     110            setSnackbarOpen(true);
     111            return;
     112        }
     113
     114        setSuggestLoading(true);
     115        try {
     116            const suggestionId = await onSuggestComponent({
     117                link: suggestForm.link,
     118                description: suggestForm.description,
     119                componentType: suggestForm.componentType
     120            });
     121
     122            setSnackbarMessage("Suggestion submitted! Admin will review it.");
     123            setSnackbarOpen(true);
     124            setSuggestOpen(false);
     125            setSuggestForm({link: '', description: '', componentType: category || ''});
     126        } catch (error) {
     127            console.error('Suggestion error:', error);
     128            setSnackbarMessage("Failed to submit suggestion. Try again.");
     129            setSnackbarOpen(true);
     130        } finally {
     131            setSuggestLoading(false);
     132        }
     133    };
    46134
    47135    let processedComponents = components.filter(comp => {
    48136        const matchesBrand = selectedBrands.length === 0 || selectedBrands.includes(comp.brand);
    49137        const matchesPrice = Number(comp.price) >= priceRange[0] && Number(comp.price) <= priceRange[1];
    50         return matchesBrand && matchesPrice;
     138        const matchesSearch = searchQuery === "" ||
     139            comp.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
     140            comp.brand.toLowerCase().includes(searchQuery.toLowerCase());
     141
     142        return matchesBrand && matchesPrice && matchesSearch;
    51143    });
    52144
     
    65157
    66158    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>
     159        <>
     160            <Dialog open={open} onClose={onClose} maxWidth="xl" fullWidth sx={{height: '90vh'}}>
     161                <AppBar position="relative" sx={{bgcolor: '#ff8201'}}>
     162                    <Toolbar>
     163                        <Typography sx={{ml: 2, flex: 1, textTransform: 'capitalize'}} variant="h6" component="div">
     164                            Browsing: <b>{category === 'gpu' ? 'Graphics Cards' : category?.toUpperCase()}</b>
     165                            {mode === 'forge' && currentBuildId && (
     166                                <Typography variant="caption"
     167                                            sx={{ml: 2, bgcolor: 'rgba(0,0,0,0.2)', px: 1, borderRadius: 1}}>
     168                                    COMPATIBILITY MODE ON
     169                                </Typography>
     170                            )}
     171                        </Typography>
     172                        <IconButton edge="start" color="inherit" onClick={onClose}>
     173                            <CloseIcon/>
     174                        </IconButton>
     175                    </Toolbar>
     176                </AppBar>
     177
     178                <DialogContent sx={{p: 0, display: 'flex', height: '100%'}}>
     179                    <Box sx={{
     180                        width: 300,
     181                        borderRight: '1px solid #ddd',
     182                        p: 3,
     183                        bgcolor: '#1e1e1e',
     184                        display: {xs: 'none', md: 'block'},
     185                        overflowY: 'auto'
     186                    }}>
     187                        <TextField
     188                            fullWidth
     189                            size="small"
     190                            placeholder="Search components..."
     191                            value={tempSearchQuery}
     192                            onChange={(e) => setTempSearchQuery(e.target.value)}
     193                            sx={{mb: 2}}
     194                            InputProps={{
     195                                startAdornment: <InputAdornment position="start"><SearchIcon/></InputAdornment>,
     196                            }}
     197                        />
     198
     199
     200                        <FormControl fullWidth size="small" sx={{mb: 2}}>
     201                            <InputLabel>Sort By</InputLabel>
     202                            <Select value={sortOrder} label="Sort By" onChange={(e) => setSortOrder(e.target.value)}>
     203                                <MenuItem value="price_asc">Price: Low to High</MenuItem>
     204                                <MenuItem value="price_desc">Price: High to Low</MenuItem>
     205                            </Select>
     206                        </FormControl>
     207
     208                        <FormControl fullWidth size="small" sx={{mb: 2}}>
     209                            <InputLabel>Brands</InputLabel>
     210                            <Select multiple value={selectedBrands} onChange={handleBrandChange} label="Brands"
     211                                    renderValue={(s) => s.join(', ')}>
     212                                {availableBrands.map((brand) => (
     213                                    <MenuItem key={brand} value={brand}>
     214                                        <Checkbox checked={selectedBrands.indexOf(brand) > -1}/>
     215                                        <ListItemText primary={brand}/>
     216                                    </MenuItem>
     217                                ))}
     218                            </Select>
     219                        </FormControl>
     220
     221                        <Typography gutterBottom fontWeight="bold">Price Range</Typography>
     222                        <Slider value={priceRange} onChange={(_, v) => setPriceRange(v as number[])} min={0} max={2000}
     223                                sx={{color: '#ff8201'}}/>
     224                        <Box sx={{display: 'flex', justifyContent: 'space-between', mt: 1}}>
     225                            <Typography variant="caption">${priceRange[0]}</Typography>
     226                            <Typography variant="caption">${priceRange[1]}+</Typography>
     227                        </Box>
     228                        {/*TO BE FINISHED*/}
     229                        <Button
     230                            fullWidth
     231                            variant="contained"
     232                            startIcon={<AddCircleIcon/>}
     233                            onClick={() => setSuggestOpen(true)}
     234                            sx={{
     235                                mt: 2,
     236                                bgcolor: '#ff8201',
     237                                fontWeight: 'bold',
     238                                fontSize: '0.875rem',
     239                                '&:hover': {bgcolor: '#e67300'}
     240                            }}
     241                        >
     242                            Suggest Component
     243                        </Button>
    96244                    </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,
     245
     246                    <Box sx={{flex: 1, p: 3, overflowY: 'auto', bgcolor: '#121212'}}>
     247                        {loading ? (
     248                            <Box sx={{display: 'flex', justifyContent: 'center', mt: 10}}>
     249                                <CircularProgress sx={{color: '#ff8201'}}/>
     250                            </Box>
     251                        ) : (
     252                            <Box sx={{
     253                                display: 'grid',
     254                                gridTemplateColumns: {
     255                                    xs: '1fr',
     256                                    sm: 'repeat(2, 1fr)',
     257                                    md: 'repeat(3, 1fr)',
     258                                    lg: 'repeat(4, 1fr)',
     259                                    xl: 'repeat(5, 1fr)'
     260                                },
     261                                gap: 3,
     262                                width: '100%'
     263                            }}>
     264                                {processedComponents.map((comp) => (
     265                                    <Box key={comp.id} sx={{width: '100%'}}>
     266                                        <Card
     267                                            elevation={3}
     268                                            sx={{
     269                                                width: '100%',
     270                                                height: '100%',
     271                                                display: 'flex',
     272                                                flexDirection: 'column',
     273                                                transition: 'transform 0.2s, box-shadow 0.2s',
     274                                                '&:hover': {
     275                                                    transform: 'translateY(-4px)',
     276                                                    boxShadow: 6
     277                                                }
     278                                            }}
     279                                        >
     280                                            <Box sx={{position: 'relative', paddingTop: '75%', bgcolor: '#1e1e1e'}}>
     281                                                <CardMedia
     282                                                    component="img"
     283                                                    image={comp.imgUrl || comp.img_url || `https://placehold.co/400x400?text=${comp.name}`}
     284                                                    alt={comp.name}
     285                                                    sx={{
     286                                                        position: 'absolute',
     287                                                        top: 0,
     288                                                        left: 0,
     289                                                        width: '100%',
     290                                                        height: '100%',
     291                                                        objectFit: 'contain',
     292                                                        p: 2
     293                                                    }}
     294                                                />
     295                                            </Box>
     296
     297                                            <CardContent sx={{
     298                                                flexGrow: 1,
     299                                                display: 'flex',
     300                                                flexDirection: 'column',
     301                                                justifyContent: 'space-between'
    184302                                            }}>
    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">
     303                                                <Box>
     304                                                    <Typography variant="caption" color="text.secondary"
     305                                                                sx={{textTransform: 'uppercase', letterSpacing: 0.5}}>
     306                                                        {comp.brand}
     307                                                    </Typography>
     308                                                    <Typography variant="subtitle1" fontWeight="bold" sx={{
     309                                                        lineHeight: 1.2,
     310                                                        mt: 0.5,
     311                                                        mb: 1,
     312                                                        overflow: 'hidden',
     313                                                        textOverflow: 'ellipsis',
     314                                                        display: '-webkit-box',
     315                                                        WebkitLineClamp: 2,
     316                                                        WebkitBoxOrient: 'vertical',
     317                                                        minHeight: '2.4em'
     318                                                    }}>
     319                                                        {comp.name}
     320                                                    </Typography>
     321                                                </Box>
     322                                                <Typography variant="h6" color="primary.main" fontWeight="bold">
    195323                                                    {formatMoney(comp.price)}
    196324                                                </Typography>
     325                                            </CardContent>
     326
     327                                            <Box sx={{p: 2, pt: 0, display: 'flex', flexDirection: 'column', gap: 1}}>
     328                                                <Button
     329                                                    fullWidth
     330                                                    variant="contained"
     331                                                    onClick={() => setSelectedComponent(comp)}
     332                                                    sx={{
     333                                                        bgcolor: '#ff8201',
     334                                                        fontWeight: 'bold',
     335                                                        '&:hover': {bgcolor: '#e67300'}
     336                                                    }}
     337                                                >
     338                                                    Details
     339                                                </Button>
     340                                                {mode === 'forge' && onSelect && (
     341                                                    <Button
     342                                                        fullWidth
     343                                                        variant="contained"
     344                                                        onClick={() => onSelect(comp)}
     345                                                        sx={{
     346                                                            bgcolor: '#4caf50',
     347                                                            fontWeight: 'bold',
     348                                                            '&:hover': {bgcolor: '#388e3c'}
     349                                                        }}
     350                                                    >
     351                                                        Add
     352                                                    </Button>
     353                                                )}
    197354                                            </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>
     355                                        </Card>
     356                                    </Box>
     357                                ))}
     358                                {processedComponents.length === 0 && (
     359                                    <Box sx={{gridColumn: '1 / -1', width: '100%', textAlign: 'center', mt: 5}}>
     360                                        <Typography variant="h6" color="text.secondary">
     361                                            No compatible components found.
     362                                        </Typography>
     363                                        {mode === 'forge' && (
     364                                            <Typography variant="body2" color="error">
     365                                                (Try removing incompatible parts)
     366                                            </Typography>
     367                                        )}
     368                                    </Box>
     369                                )}
     370                            </Box>
     371                        )}
     372                    </Box>
     373                </DialogContent>
     374
     375                <ComponentDetailsDialog
     376                    open={!!selectedComponent}
     377                    component={selectedComponent}
     378                    onClose={() => setSelectedComponent(null)}
     379                />
     380            </Dialog>
     381
     382            <Dialog open={suggestOpen} onClose={() => setSuggestOpen(false)} maxWidth="sm" fullWidth>
     383                <DialogTitle>Suggest a New Component</DialogTitle>
     384                <DialogContent>
     385                    <MuiTextField
     386                        fullWidth
     387                        label="Product Link *"
     388                        name="link"
     389                        value={suggestForm.link}
     390                        onChange={handleSuggestChange}
     391                        placeholder="https://example.com/product"
     392                        sx={{mt: 1}}
     393                        size="small"
     394                    />
     395                    <MuiTextField
     396                        fullWidth
     397                        label="Description *"
     398                        name="description"
     399                        value={suggestForm.description}
     400                        onChange={handleSuggestChange}
     401                        multiline
     402                        rows={3}
     403                        placeholder="Why should we add this component? Any specs/details?"
     404                        sx={{mt: 2}}
     405                        size="small"
     406                    />
     407                    <Typography variant="caption" color="text.secondary" sx={{mt: 1, display: 'block'}}>
     408                        Category: <b>{category?.toUpperCase()}</b>
     409                    </Typography>
     410                </DialogContent>
     411                <DialogActions>
     412                    <Button onClick={() => setSuggestOpen(false)}>Cancel</Button>
     413                    <Button
     414                        onClick={submitSuggestion}
     415                        disabled={suggestLoading || !userId}
     416                        variant="contained"
     417                        startIcon={suggestLoading ? <CircularProgress size={20}/> : null}
     418                    >
     419                        {suggestLoading ? 'Submitting...' : 'Submit Suggestion'}
     420                    </Button>
     421                </DialogActions>
     422            </Dialog>
     423
     424            <Snackbar
     425                open={snackbarOpen}
     426                autoHideDuration={4000}
     427                onClose={() => setSnackbarOpen(false)}
     428                anchorOrigin={{vertical: 'bottom', horizontal: 'right'}}
     429            >
     430                <Alert onClose={() => setSnackbarOpen(false)} sx={{width: '100%'}}>
     431                    {snackbarMessage}
     432                </Alert>
     433            </Snackbar>
     434        </>
    232435    );
    233436}
Note: See TracChangeset for help on using the changeset viewer.