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/BuildDetailsDialog.tsx

    rae5d054 r8a7f936  
    11import React, { useEffect, useState } from 'react';
    22import {
    3     Dialog, DialogTitle, DialogContent, Button, Grid, Box, Typography,
    4     IconButton, Tab, Tabs, Table, TableBody, TableCell, TableRow, Rating, TextField, Avatar, Chip
     3    Dialog, DialogTitle, DialogContent, DialogActions, Button, Grid, Box, Typography,
     4    IconButton, Tab, Tabs, Table, TableBody, TableCell, TableRow, Rating, TextField, Avatar, Chip, Alert
    55} from '@mui/material';
    66import CloseIcon from '@mui/icons-material/Close';
     
    99import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
    1010import PersonIcon from "@mui/icons-material/Person";
    11 import { onGetBuildDetails, onSetReview, onToggleFavorite } from '../pages/+Layout.telefunc';
     11import {onGetBuildDetails, onSetReview, onToggleFavorite, onCloneBuild, onSetRating} from '../pages/+Layout.telefunc';
    1212
    1313const formatPrice = (price: any) => new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(Number(price) || 0);
    1414
    15 export default function BuildDetailsDialog({ open, buildId, onClose, onClone, currentUser }: any) {
     15export default function BuildDetailsDialog({ open, buildId, onClose, currentUser }: any) {
    1616    const [details, setDetails] = useState<any>(null);
    1717    const [loading, setLoading] = useState(false);
    1818    const [tabIndex, setTabIndex] = useState(0);
     19    const [cloneDialogOpen, setCloneDialogOpen] = useState(false);
     20    const [cloningBuildId, setCloningBuildId] = useState<number | null>(null);
    1921
    2022    const [reviewText, setReviewText] = useState("");
     
    4547    const handleSubmitReview = async () => {
    4648        if (!currentUser) return alert("Please login to review.");
    47         await onSetReview({ buildId, content: reviewText });
    48 
    49         setReviewText("");
     49
     50        await onSetReview({
     51            buildId,
     52            content: reviewText,
     53            // rating: ratingVal
     54        });
     55
     56        await onSetRating({
     57            buildId,
     58            value: ratingVal
     59        })
    5060
    5161        const refreshed = await onGetBuildDetails({ buildId });
     
    5363    };
    5464
     65    const handleCloneConfirm = async () => {
     66        if (!cloningBuildId) return;
     67
     68        try {
     69            const newBuildId = await onCloneBuild({ buildId: cloningBuildId });
     70
     71            window.location.href = `/forge?buildId=${newBuildId}`;
     72
     73            setCloneDialogOpen(false);
     74            setCloningBuildId(null);
     75        } catch (e) {
     76            alert("Failed to clone build. Please try again.");
     77        }
     78    };
     79
     80
    5581    if (!open) return null;
    5682
    57     // @ts-ignore
    5883    return (
    59         <Dialog open={open} onClose={onClose} maxWidth="md" fullWidth scroll="paper">
    60             {loading || !details ? (
    61                 <Box sx={{ p: 5, textAlign: 'center' }}>Loading Forge Schematics...</Box>
    62             ) : (
    63                 <>
    64                     <DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', bgcolor: '#ff8201' }}>
    65                         <Box>
    66                             <Typography variant="h5" fontWeight="bold">{details.name}</Typography>
    67                             <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
    68                                 <PersonIcon sx={{ fontSize: 16 }} />
    69                                 <Typography variant="subtitle2" color="text.secondary" fontWeight="bold">
    70                                     by {details.creator}
    71                                 </Typography>
    72                                 <Chip label={formatPrice(details.totalPrice)} size="small" color="primary" variant="outlined" />
     84        <>
     85            <Dialog open={open} onClose={onClose} maxWidth="md" fullWidth scroll="paper">
     86                {loading || !details ? (
     87                    <Box sx={{ p: 5, textAlign: 'center' }}>Loading Forge Schematics...</Box>
     88                ) : (
     89                    <>
     90                        <DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', bgcolor: '#ff8201' }}>
     91                            <Box>
     92                                <Typography variant="h5" fontWeight="bold">{details.name}</Typography>
     93                                <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
     94                                    <PersonIcon sx={{ fontSize: 16 }} />
     95                                    <Typography variant="subtitle2" color="text.secondary" fontWeight="bold">
     96                                        by {details.creator}
     97                                    </Typography>
     98                                    <Chip label={formatPrice(details.totalPrice)} size="small" color="primary" variant="outlined" />
     99                                </Box>
    73100                            </Box>
    74                         </Box>
    75                         <IconButton onClick={onClose}><CloseIcon /></IconButton>
    76                     </DialogTitle>
    77 
    78                     <DialogContent sx={{ p: 0 }}>
    79                         <Box sx={{ borderBottom: 1, borderColor: 'divider', px: 2, bgcolor: 'primary', position: 'sticky', top: 0, zIndex: 1 }}>
    80                             <Tabs value={tabIndex} onChange={(_, v) => setTabIndex(v)}>
    81                                 <Tab label="Specs" />
    82                                 <Tab label={`Reviews (${details.ratingStatistics.ratingCount})`} />
    83                             </Tabs>
    84                         </Box>
    85 
    86                         <Box sx={{ p: 3 }}>
    87                             {tabIndex === 0 && (
    88                                 <Grid container spacing={2}>
    89                                     <Grid item xs={12} md={8}>
    90                                         <Table size="small">
    91                                             <TableBody>
    92                                                 {details.components.map((comp: any) => (
    93                                                     <TableRow key={comp.id}>
    94                                                         <TableCell sx={{ width: 50 }}>
    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>
    102                                                         </TableCell>
    103                                                         <TableCell>
    104                                                             <Typography variant="body2" color="text.secondary" sx={{ fontSize: '0.75rem', textTransform: 'uppercase' }}>
    105                                                                 {comp.type}
    106                                                             </Typography>
    107                                                             <Typography variant="body1" fontWeight="500">
    108                                                                 {comp.brand} {comp.name}
    109                                                             </Typography>
    110                                                         </TableCell>
    111                                                         <TableCell align="right" sx={{ fontWeight: 'bold', color: '#ff8201' }}>
    112                                                             {formatPrice(comp.price)}
     101                            <IconButton onClick={onClose}><CloseIcon /></IconButton>
     102                        </DialogTitle>
     103
     104                        <DialogContent sx={{ p: 0 }}>
     105                            <Box sx={{ borderBottom: 1, borderColor: 'divider', px: 2, bgcolor: 'primary', position: 'sticky', top: 0, zIndex: 1 }}>
     106                                <Tabs value={tabIndex} onChange={(_, v) => setTabIndex(v)}>
     107                                    <Tab label="Specs" />
     108                                    <Tab label={`Reviews (${details.ratingStatistics.ratingCount})`} />
     109                                </Tabs>
     110                            </Box>
     111
     112                            <Box sx={{ p: 3 }}>
     113                                {tabIndex === 0 && (
     114                                    <Grid container spacing={2}>
     115                                        <Grid item xs={12} md={8}>
     116                                            <Table size="small">
     117                                                <TableBody>
     118                                                    {details.components.map((comp: any) => (
     119                                                        <TableRow key={comp.id}>
     120                                                            <TableCell sx={{ width: 50 }}>
     121                                                                <Avatar
     122                                                                    src={comp.img_url || undefined}
     123                                                                    variant="rounded"
     124                                                                    sx={{ width: 45, height: 45, bgcolor: '#ff8201'}}
     125                                                                >
     126                                                                    {comp.type?.substring(0, 3)?.toUpperCase()}
     127                                                                </Avatar>
     128                                                            </TableCell>
     129                                                            <TableCell>
     130                                                                <Typography variant="body2" color="text.secondary" sx={{ fontSize: '0.75rem', textTransform: 'uppercase' }}>
     131                                                                    {comp.type}
     132                                                                </Typography>
     133                                                                <Typography variant="body1" fontWeight="500">
     134                                                                    {comp.brand} {comp.name}
     135                                                                </Typography>
     136                                                            </TableCell>
     137                                                            <TableCell align="right" sx={{ fontWeight: 'bold', color: '#ff8201' }}>
     138                                                                {formatPrice(comp.price)}
     139                                                            </TableCell>
     140                                                        </TableRow>
     141                                                    ))}
     142                                                    <TableRow sx={{ bgcolor: '#424343' }}>
     143                                                        <TableCell colSpan={2} sx={{ fontWeight: 'bold', color: '#ff8201' }}>TOTAL</TableCell>
     144                                                        <TableCell align="right" sx={{ fontWeight: 'bold', fontSize: '1.1rem', color: 'primary.main' }}>
     145                                                            {formatPrice(details.totalPrice)}
    113146                                                        </TableCell>
    114147                                                    </TableRow>
    115                                                 ))}
    116                                                 <TableRow sx={{ bgcolor: '#424343' }}>
    117                                                     <TableCell colSpan={2} sx={{ fontWeight: 'bold', color: '#ff8201' }}>TOTAL</TableCell>
    118                                                     <TableCell align="right" sx={{ fontWeight: 'bold', fontSize: '1.1rem', color: 'primary.main' }}>
    119                                                         {formatPrice(details.totalPrice)}
    120                                                     </TableCell>
    121                                                 </TableRow>
    122                                             </TableBody>
    123                                         </Table>
     148                                                </TableBody>
     149                                            </Table>
     150                                        </Grid>
     151
     152                                        <Grid item xs={12} md={4}>
     153                                            <Box sx={{ bgcolor: '#424343', p: 2, borderRadius: 2, mb: 2 }}>
     154                                                <Typography color="primary.main" gutterBottom fontWeight="bold">Builder's Notes</Typography>
     155                                                <Typography color="primary.main" variant="body2" sx={{ fontStyle: 'italic' }}>
     156                                                    "{details.description || "No notes provided."}"
     157                                                </Typography>
     158                                            </Box>
     159
     160                                            <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
     161                                                <Button
     162                                                    variant="contained"
     163                                                    color="primary"
     164                                                    size="large"
     165                                                    startIcon={<AutoFixHighIcon />}
     166                                                    onClick={() => {
     167                                                        setCloningBuildId(details.id);
     168                                                        setCloneDialogOpen(true);
     169                                                    }}
     170                                                >
     171                                                    Clone & Edit
     172                                                </Button>
     173                                                <Button
     174                                                    variant={details.isFavorite ? "contained" : "outlined"}
     175                                                    color={details.isFavorite ? "error" : "primary"}
     176                                                    startIcon={details.isFavorite ? <FavoriteIcon /> : <FavoriteBorderIcon />}
     177                                                    onClick={handleFavorite}
     178                                                >
     179                                                    {details.isFavorite ? "Favorited" : "Add to Favorites"}
     180                                                </Button>
     181                                            </Box>
     182                                        </Grid>
    124183                                    </Grid>
    125 
    126                                     <Grid item xs={12} md={4}>
    127                                         <Box sx={{ bgcolor: '#424343', p: 2, borderRadius: 2, mb: 2 }}>
    128                                             <Typography color="primary.main" gutterBottom fontWeight="bold">Builder's Notes</Typography>
    129                                             <Typography color="primary.main" variant="body2" sx={{ fontStyle: 'italic' }}>
    130                                                 "{details.description || "No notes provided."}"
    131                                             </Typography>
     184                                )}
     185
     186                                {tabIndex === 1 && (
     187                                    <Box>
     188                                        <Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mb: 4, p: 2, bgcolor: '#5e5e5e', borderRadius: 2 }}>
     189                                            <Typography variant="h3" fontWeight="bold">{details.ratingStatistics.averageRating.toFixed(1)}</Typography>
     190                                            <Box>
     191                                                <Rating value={details.ratingStatistics.averageRating} readOnly precision={0.5} />
     192                                                <Typography variant="body2" color="text.secondary">{details.ratingStatistics.ratingCount} ratings</Typography>
     193                                            </Box>
    132194                                        </Box>
    133195
    134                                         <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
    135                                             <Button
    136                                                 variant="contained"
    137                                                 color="primary"
    138                                                 size="large"
    139                                                 startIcon={<AutoFixHighIcon />}
    140                                                 onClick={() => onClone({ buildId: details.id })}
    141                                             >
    142                                                 Clone & Edit
    143                                             </Button>
    144                                             <Button
    145                                                 variant={details.isFavorite ? "contained" : "outlined"}
    146                                                 color={details.isFavorite ? "error" : "primary"}
    147                                                 startIcon={details.isFavorite ? <FavoriteIcon /> : <FavoriteBorderIcon />}
    148                                                 onClick={handleFavorite}
    149                                             >
    150                                                 {details.isFavorite ? "Favorited" : "Add to Favorites"}
    151                                             </Button>
    152                                         </Box>
    153                                     </Grid>
    154                                 </Grid>
    155                             )}
    156 
    157                             {tabIndex === 1 && (
    158                                 <Box>
    159                                     <Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mb: 4, p: 2, bgcolor: '#5e5e5e', borderRadius: 2 }}>
    160                                         <Typography variant="h3" fontWeight="bold">{details.ratingStatistics.averageRating.toFixed(1)}</Typography>
    161                                         <Box>
    162                                             <Rating value={details.ratingStatistics.averageRating} readOnly precision={0.5} />
    163                                             <Typography variant="body2" color="text.secondary">{details.ratingStatistics.ratingCount} ratings</Typography>
     196                                        {currentUser && details.userId !== currentUser.id && (
     197                                            <Box sx={{ mb: 4, p: 2, border: '1px solid #ddd', borderRadius: 2 }}>
     198                                                <Typography variant="subtitle2" gutterBottom>Your Review</Typography>
     199                                                <Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
     200                                                    <Rating value={ratingVal} onChange={(_, v) => setRatingVal(v || 5)} />
     201                                                </Box>
     202                                                <TextField
     203                                                    fullWidth
     204                                                    multiline
     205                                                    rows={2}
     206                                                    placeholder="Share your thoughts on this build..."
     207                                                    value={reviewText}
     208                                                    onChange={(e) => setReviewText(e.target.value)}
     209                                                    sx={{ mb: 1 }}
     210                                                />
     211                                                <Button size="small" variant="contained" onClick={handleSubmitReview}>
     212                                                    Submit Review
     213                                                </Button>
     214                                            </Box>
     215                                        )}
     216
     217                                        {currentUser && details.userId === currentUser.id && (
     218                                            <Alert severity="info" sx={{ mb: 4 }}>
     219                                                You cannot rate your own builds.
     220                                            </Alert>
     221                                        )}
     222
     223                                        <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
     224                                            {details.reviews.map((rev: any, i: number) => (
     225                                                <Box key={i} sx={{ pb: 2, borderBottom: '1px solid #eee' }}>
     226                                                    <Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 0.5 }}>
     227                                                        <Typography fontWeight="bold" variant="body2">{rev.username}</Typography>
     228                                                        <Typography variant="caption" color="text.secondary">{rev.createdAt}</Typography>
     229                                                    </Box>
     230                                                    <Typography variant="body2">{rev.content}</Typography>
     231                                                </Box>
     232                                            ))}
     233                                            {details.reviews.length === 0 && (
     234                                                <Typography color="text.secondary" align="center">No reviews yet. Be the first!</Typography>
     235                                            )}
    164236                                        </Box>
    165237                                    </Box>
    166 
    167                                     {currentUser && (
    168                                         <Box sx={{ mb: 4, p: 2, border: '1px solid #ddd', borderRadius: 2 }}>
    169                                             <Typography variant="subtitle2" gutterBottom>Your Review</Typography>
    170                                             <Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
    171                                                 <Rating value={ratingVal} onChange={(_, v) => setRatingVal(v || 5)} />
    172                                             </Box>
    173                                             <TextField
    174                                                 fullWidth
    175                                                 multiline
    176                                                 rows={2}
    177                                                 placeholder="Share your thoughts on this build..."
    178                                                 value={reviewText}
    179                                                 onChange={(e) => setReviewText(e.target.value)}
    180                                                 sx={{ mb: 1 }}
    181                                             />
    182                                             <Button size="small" variant="contained" onClick={handleSubmitReview}>
    183                                                 Submit Review
    184                                             </Button>
    185                                         </Box>
    186                                     )}
    187 
    188                                     <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2}}>
    189                                         {details.reviews.map((rev: any, i: number) => (
    190                                             <Box key={i} sx={{ pb: 2, borderBottom: '1px solid #eee'}}>
    191                                                 <Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 0.5 }}>
    192                                                     <Typography fontWeight="bold" variant="body2">{rev.username}</Typography>
    193                                                     <Typography variant="caption" color="text.secondary">{rev.createdAt}</Typography>
    194                                                 </Box>
    195                                                 <Typography variant="body2">{rev.content}</Typography>
    196                                             </Box>
    197                                         ))}
    198                                         {details.reviews.length === 0 && (
    199                                             <Typography color="text.secondary" align="center">No reviews yet. Be the first!</Typography>
    200                                         )}
    201                                     </Box>
    202                                 </Box>
    203                             )}
    204                         </Box>
    205                     </DialogContent>
    206                 </>
    207             )}
    208         </Dialog>
     238                                )}
     239                            </Box>
     240                        </DialogContent>
     241                    </>
     242                )}
     243            </Dialog>
     244
     245            <Dialog open={cloneDialogOpen} onClose={() => setCloneDialogOpen(false)}>
     246                <DialogTitle>Clone Build</DialogTitle>
     247                <DialogContent>
     248                    <Typography>
     249                        This will create a copy of "{details?.name}" in your Forge so you can edit it.
     250                        All components will be copied over.
     251                    </Typography>
     252                </DialogContent>
     253                <DialogActions>
     254                    <Button onClick={() => setCloneDialogOpen(false)}>Cancel</Button>
     255                    <Button
     256                        variant="contained"
     257                        onClick={handleCloneConfirm}
     258                        disabled={!cloningBuildId}
     259                    >
     260                        Clone Build
     261                    </Button>
     262                </DialogActions>
     263            </Dialog>
     264        </>
    209265    );
    210266}
Note: See TracChangeset for help on using the changeset viewer.