Changes in / [92fafb3:c586cbd]


Ignore:
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • components/BuildDetailsDialog.tsx

    r92fafb3 rc586cbd  
    11import React, {useEffect, useState} from 'react';
    22import {
    3     Dialog, DialogTitle, DialogContent, DialogActions, Button, Box, Typography,
     3    Dialog, DialogTitle, DialogContent, DialogActions, Button, Grid, Box, Typography,
    44    IconButton, Tab, Tabs, Table, TableBody, TableCell, TableRow, Rating, TextField, Avatar, Chip, Alert
    55} from '@mui/material';
     
    3434    const [ratingVal, setRatingVal] = useState(5);
    3535
     36    // Main details fetch
    3637    useEffect(() => {
    37         if (open && buildId !== null) {
     38        if (open && buildId !== null && typeof buildId === 'number') {
    3839            setLoading(true);
    3940            setReviewText("");
     
    5152
    5253    useEffect(() => {
    53         if (open && buildId !== null) {
     54        if (open && buildId !== null && typeof buildId === 'number') {
    5455            onGetBuildState({buildId})
    5556                .then(state => {
     
    6263    }, [open, buildId]);
    6364
    64     useEffect(() => {
    65         if (open) {
    66             setTabIndex(0);
    67         }
    68     }, [open, buildId]);
    69 
    70 
    7165    const handleFavorite = async () => {
    7266        if (!currentUser || buildId === null) return alert("Please login to favorite builds.");
     
    109103    return (
    110104        <>
    111             <Dialog open={open} onClose={onClose} maxWidth="md" fullWidth scroll="body">
     105            <Dialog open={open} onClose={onClose} maxWidth="md" fullWidth scroll="paper">
    112106                {loading || !details ? (
    113107                    <Box sx={{p: 5, textAlign: 'center'}}>Loading Forge Schematics...</Box>
     
    152146                            <Box sx={{p: 3}}>
    153147                                {tabIndex === 0 && (
    154                                     <Box
    155                                         sx={{
    156                                             display: 'grid',
    157                                             gridTemplateColumns: {
    158                                                 xs: '1fr',
    159                                                 md: '2fr 1fr'
    160                                             },
    161                                             gap: 2,
    162                                             width: '100%'
    163                                         }}
    164                                     >
    165                                         <Box>
     148                                    <Grid container spacing={2}>
     149                                        <Grid item xs={12} md={8}>
    166150                                            <Table size="small">
    167151                                                <TableBody>
     
    209193                                                </TableBody>
    210194                                            </Table>
    211                                         </Box>
    212 
    213                                         <Box>
     195                                        </Grid>
     196
     197                                        <Grid item xs={12} md={4}>
    214198                                            <Box sx={{bgcolor: '#424343', p: 2, borderRadius: 2, mb: 2}}>
    215                                                 <Typography color="primary.main" gutterBottom fontWeight="bold">
    216                                                     Builder's Notes
    217                                                 </Typography>
     199                                                <Typography color="primary.main" gutterBottom fontWeight="bold">Builder's
     200                                                    Notes</Typography>
    218201                                                <Typography color="primary.main" variant="body2"
    219202                                                            sx={{fontStyle: 'italic'}}>
     
    260243                                                </Button>
    261244                                            </Box>
    262                                         </Box>
    263                                     </Box>
     245                                        </Grid>
     246                                    </Grid>
    264247                                )}
    265248
     
    330313                                            ))}
    331314                                            {details.reviews.length === 0 && (
    332                                                 <Typography color="text.secondary" align="center">
    333                                                     No reviews yet. Be the first!
    334                                                 </Typography>
     315                                                <Typography color="text.secondary" align="center">No reviews yet. Be the
     316                                                    first!</Typography>
    335317                                            )}
    336318                                        </Box>
  • components/ComponentDetailsDialog.tsx

    r92fafb3 rc586cbd  
    22import {
    33    Dialog, DialogTitle, DialogContent, DialogActions, Button,
    4     Typography, Box, Chip, CircularProgress, IconButton,
     4    Typography, Box, Grid, Chip, CircularProgress, IconButton,
    55    Table, TableBody, TableCell, TableContainer, TableRow, Paper
    66} from '@mui/material';
     
    9292            open={open}
    9393            onClose={onClose}
    94             maxWidth="md"
    95             fullWidth
    96             scroll="body"
     94            maxWidth="lg"
     95            // fullWidth
    9796            sx={{zIndex: 1400}}
    9897        >
     
    117116            </DialogTitle>
    118117
    119             <DialogContent
    120                 sx={{
    121                     p: 1,
    122                     overflow: 'visible'
    123                 }}
    124             >
     118            <DialogContent dividers>
    125119                {loading ? (
    126120                    <Box sx={{display: 'flex', justifyContent: 'center', p: 5}}>
     
    128122                    </Box>
    129123                ) : (
    130                     <Box
    131                         sx={{
    132                             display: 'grid',
    133                             gridTemplateColumns: {
    134                                 xs: '1fr',
    135                                 md: '1fr 1fr'
    136                             },
    137                             gap: 4,
    138                             width: '100%'
    139                         }}
    140                     >
    141                         <Box sx={{display: 'flex', flexDirection: 'column', alignItems: 'center'}}>
     124                    <Grid container spacing={4}>
     125                        <Grid item xs={12} md={4} sx={{display: 'flex', flexDirection: 'column', alignItems: 'center'}}>
    142126                            <Box
    143127                                component="img"
     128                                // src={`https://placehold.co/400x400?text=${encodeURIComponent(displayData.name)}`}
    144129                                src={displayData.imgUrl}
    145130                                alt={displayData.name}
     
    148133                                    maxHeight: 300,
    149134                                    objectFit: 'contain',
    150                                     mb: 1,
    151                                     mt: 7,
    152                                     p: 1
     135                                    mb: 2,
     136                                    // border: '1px solid #eee',
     137                                    // borderRadius: 2,
     138                                    p: 2
    153139                                }}
    154140                            />
     
    158144                                sx={{fontWeight: 'bold'}}
    159145                            />
    160                         </Box>
    161 
    162                         <Box sx={{
    163                             display: 'flex',
    164                             flexDirection: 'column',
    165                             alignItems: 'center',
    166                             width: '70%'
    167                         }}>
    168                             <Typography
    169                                 variant="subtitle1"
    170                                 fontWeight="bold"
    171                                 gutterBottom
    172                                 sx={{
    173                                     borderBottom: '2px solid #ff8201',
    174                                     mb: 1,
    175                                     mt: 1,
    176                                     textAlign: 'center',
    177                                     width: '100%'
    178                                 }}
    179                             >
     146                        </Grid>
     147
     148                        <Grid item xs={12} md={8}>
     149                            <Typography variant="subtitle1" fontWeight="bold" gutterBottom
     150                                        sx={{borderBottom: '2px solid #ff8201', display: 'inline-block', mb: 0.5}}>
    180151                                Technical Specifications
    181152                            </Typography>
    182153
    183                             <TableContainer
    184                                 component={Paper}
    185                                 variant="outlined"
    186                                 sx={{
    187                                     maxWidth: '100%',
    188                                     width: '100%'
    189                                 }}
    190                             >
    191                                 <Table size="medium">
     154                            <TableContainer component={Paper} variant="outlined">
     155                                <Table size="small">
    192156                                    <TableBody>
    193157                                        <TableRow>
    194                                             <TableCell
    195                                                 component="th"
    196                                                 scope="row"
    197                                                 sx={{
    198                                                     fontWeight: 'bold',
    199                                                     width: '50%',
    200                                                     bgcolor: '#1e1e1e',
    201                                                     textAlign: 'center',
    202                                                     px: 2
    203                                                 }}
    204                                             >
     158                                            <TableCell component="th" scope="row"
     159                                                       sx={{fontWeight: 'bold', width: '30%', bgcolor: '#1e1e1e'}}>
    205160                                                Brand
    206161                                            </TableCell>
    207                                             <TableCell sx={{
    208                                                 textAlign: 'center',
    209                                                 px: 2,
    210                                                 width: '50%'
    211                                             }}>
    212                                                 {displayData.brand}
    213                                             </TableCell>
     162                                            <TableCell>{displayData.brand}</TableCell>
    214163                                        </TableRow>
    215164
     
    219168                                            return (
    220169                                                <TableRow key={key}>
    221                                                     <TableCell
    222                                                         component="th"
    223                                                         scope="row"
    224                                                         sx={{
    225                                                             fontWeight: 'bold',
    226                                                             width: '50%',
    227                                                             bgcolor: '#1e1e1e',
    228                                                             textAlign: 'center',
    229                                                             px: 2
    230                                                         }}
    231                                                     >
     170                                                    <TableCell component="th" scope="row" sx={{
     171                                                        fontWeight: 'bold',
     172                                                        width: '30%',
     173                                                        bgcolor: '#1e1e1e'
     174                                                    }}>
    232175                                                        {formatKey(key)}
    233176                                                    </TableCell>
    234                                                     <TableCell sx={{
    235                                                         textAlign: 'center',
    236                                                         px: 2,
    237                                                         width: '50%'
    238                                                     }}>
    239                                                         {renderValue(key, val)}
    240                                                     </TableCell>
     177                                                    <TableCell>{renderValue(key, val)}</TableCell>
    241178                                                </TableRow>
    242179                                            );
     
    245182                                </Table>
    246183                            </TableContainer>
    247 
    248                             <Box sx={{
    249                                 display: 'flex',
    250                                 justifyContent: 'center',
    251                                 alignItems: 'center',
    252                                 mt: 1,
    253                                 width: '100%'
    254                             }}>
     184                            <Box sx={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', mt: 0.5}}>
    255185                                <Typography variant="h4" color="primary.main" fontWeight="bold">
    256186                                    {formatMoney(displayData.price)}
    257187                                </Typography>
    258188                            </Box>
    259                         </Box>
    260                     </Box>
     189                        </Grid>
     190                    </Grid>
    261191                )}
    262192            </DialogContent>
     
    268198                            color: 'white',
    269199                            borderColor: '#ff8201',
    270                             '&:hover': {backgroundColor: '#ba5d02', borderColor: '#ba5d02'}
     200                            onHover: {backgroundColor: '#ba5d02', borderColor: '#ba5d02'}
    271201                        }}
    272202                >Close</Button>
  • components/ComponentDialog.tsx

    r92fafb3 rc586cbd  
    5757        return () => clearTimeout(timeoutId);
    5858    }, [tempSearchQuery]);
    59 
    60     useEffect(() => {
    61         if (open && category) {
    62             setSelectedBrands([]);
    63             setSortOrder('price_desc');
    64             setTempSearchQuery('');
    65             setSearchQuery('');
    66             setPriceRange([0, 2000]);
    67         }
    68     }, [open, category]);
    69 
    7059
    7160    useEffect(() => {
     
    308297                                Browsing:
    309298                            </Box>
    310                             <b> {category === 'gpu' ? 'GRAPHICS CARDS' : category === 'memory_card' ? 'STORAGE EXPANSION CARDS' : category?.toUpperCase()}</b>
     299                            <b> {category === 'gpu' ? 'Graphics Cards' : category?.toUpperCase()}</b>
    311300                            {mode === 'forge' && currentBuildId && (
    312301                                <Typography
  • components/Navbar.tsx

    r92fafb3 rc586cbd  
    6464    { id: 'sound_card', label: 'Sound Cards', icon: <SpeakerIcon fontSize="small" /> },
    6565    { id: 'optical_drive', label: 'Optical Drives', icon: <AlbumIcon fontSize="small" /> },
    66     { id: 'memory_card', label: 'Storage Cards', icon: <SdCardIcon fontSize="small" /> },
     66    { id: 'memory_card', label: 'Memory Cards', icon: <SdCardIcon fontSize="small" /> },
    6767    { id: 'cables', label: 'Cables', icon: <CableIcon fontSize="small" /> },
    6868];
  • pages/completed-builds/+Page.tsx

    r92fafb3 rc586cbd  
    99import BuildCard from '../../components/BuildCard';
    1010import BuildDetailsDialog from '../../components/BuildDetailsDialog';
    11 import { onGetApprovedBuilds, onGetAuthState } from '../+Layout.telefunc';
     11import { onGetApprovedBuilds, onCloneBuild, onGetAuthState } from '../+Layout.telefunc';
    1212
    1313export default function CompletedBuildsPage() {
     
    6565
    6666    useEffect(() => {
    67         void loadBuilds();
     67        loadBuilds();
    6868    }, [sortBy, searchQuery]);
     69
     70    const handleClone = async (buildId: number) => {
     71        if (!userId) return alert("Please login to clone builds!");
     72        if (confirm(`Clone this build?`)) {
     73            await onCloneBuild({buildId});
     74            alert("Build cloned!");
     75            setSelectedBuildId(null);
     76        }
     77    };
    6978
    7079    return (
     
    93102                            value={searchQuery}
    94103                            onChange={(e) => setSearchQuery(e.target.value)}
    95                             slotProps={{
    96                                 input: {
    97                                     startAdornment: <InputAdornment position="start"><SearchIcon /></InputAdornment>,
    98                                 }
     104                            InputProps={{
     105                                startAdornment: <InputAdornment position="start"><SearchIcon /></InputAdornment>,
    99106                            }}
    100107                            sx={{ mb: 3 }}
     
    186193                currentUser={userId}
    187194                onClose={() => setSelectedBuildId(null)}
     195                onClone={handleClone}
    188196            />
    189197        </Container>
  • pages/dashboard/admin/+Page.tsx

    r92fafb3 rc586cbd  
    11import React, {useEffect, useState} from 'react';
    22import {
    3     Container, Paper, Typography, Box, Avatar, Divider, Button,
     3    Container, Grid, Paper, Typography, Box, Avatar, Divider, Button,
    44    CircularProgress, Table, TableBody, TableCell, TableHead, TableRow,
    55    Chip, IconButton, TextField, Dialog, DialogTitle, DialogContent, DialogActions
     
    173173    return (
    174174        <Container maxWidth="xl" sx={{mt: 4, mb: 10}}>
    175             <Box sx={{
    176                 display: 'grid',
    177                 gridTemplateColumns: {
    178                     xs: '1fr',
    179                     md: '300px 1fr'
    180                 },
    181                 gap: 4
    182             }}>
    183                 <Box>
     175            <Grid container spacing={4}>
     176                <Grid item xs={12} md={3}>
    184177                    <Paper elevation={3} sx={{
    185178                        p: 4,
     
    223216                        </Box>
    224217                    </Paper>
    225                 </Box>
    226 
    227                 <Box sx={{
    228                     display: 'flex',
    229                     flexDirection: 'column',
    230                     gap: 4
    231                 }}>
    232                     <Paper sx={{p: 3, borderLeft: '6px solid #9c27b0'}}>
    233                         <Box sx={{display: 'flex', alignItems: 'center', mb: 2}}>
    234                             <MemoryIcon color="secondary" sx={{mr: 1}}/>
    235                             <Typography variant="h6" fontWeight="bold">
    236                                 Component Suggestions ({data.componentSuggestions?.length || 0})
    237                             </Typography>
    238                         </Box>
    239 
    240                         {data.componentSuggestions?.length === 0 ? (
    241                             <Typography color="text.secondary">No suggestions.</Typography>
    242                         ) : (
    243                             <Box sx={{width: '100%', overflowX: 'auto'}}>
    244                                 <Table size="small">
    245                                     <TableHead>
    246                                         <TableRow>
    247                                             <TableCell>Link/Description</TableCell>
    248                                             <TableCell>Type</TableCell>
    249                                             <TableCell>User</TableCell>
    250                                             <TableCell>Status</TableCell>
    251                                             <TableCell align="right">Actions</TableCell>
    252                                         </TableRow>
    253                                     </TableHead>
    254                                     <TableBody>
    255                                         {data.componentSuggestions?.map((sug: any) => (
    256                                             <TableRow key={sug.id}>
    257                                                 <TableCell sx={{minWidth: 200, maxWidth: 300}}>
    258                                                     <a
    259                                                         href={sug.link}
    260                                                         target="_blank"
    261                                                         rel="noopener noreferrer"
    262                                                         title={sug.link}
    263                                                         style={{textDecoration: 'none', color: 'inherit'}}
    264                                                     >
    265                                                         {sug.description || sug.link}
    266                                                     </a>
    267                                                 </TableCell>
    268                                                 <TableCell sx={{minWidth: 80}}>
    269                                                     {sug.componentType?.toUpperCase()}
    270                                                 </TableCell>
    271                                                 <TableCell sx={{minWidth: 100}}>
    272                                                     {sug.user?.username || `${sug.userId}`}
    273                                                 </TableCell>
    274                                                 <TableCell sx={{minWidth: 100}}>
    275                                                     <Chip
    276                                                         label={sug.status || 'pending'}
    277                                                         size="small"
    278                                                         color={
    279                                                             sug.status === 'approved' ? 'success' :
    280                                                                 sug.status === 'rejected' ? 'error' :
    281                                                                     'default'
    282                                                         }
     218                </Grid>
     219
     220                <Grid item xs={12} md={9}>
     221                    <Grid container spacing={4} direction="column">
     222                        <Grid item xs={12}>
     223                            <Paper sx={{p: 3, borderLeft: '6px solid #9c27b0'}}>
     224                                <Box sx={{display: 'flex', alignItems: 'center', mb: 2}}>
     225                                    <MemoryIcon color="secondary" sx={{mr: 1}}/>
     226                                    <Typography variant="h6" fontWeight="bold">
     227                                        Component Suggestions ({data.componentSuggestions?.length || 0})
     228                                    </Typography>
     229                                </Box>
     230
     231                                {data.componentSuggestions?.length === 0 ? (
     232                                    <Typography color="text.secondary">No suggestions.</Typography>
     233                                ) : (
     234                                    <Table size="small">
     235                                        <TableHead>
     236                                            <TableRow>
     237                                                <TableCell>Link/Description</TableCell>
     238                                                <TableCell>Type</TableCell>
     239                                                <TableCell>User</TableCell>
     240                                                <TableCell>Status</TableCell>
     241                                                <TableCell align="right">Actions</TableCell>
     242                                            </TableRow>
     243                                        </TableHead>
     244                                        <TableBody>
     245                                            {data.componentSuggestions?.map((sug: any) => (
     246                                                <TableRow key={sug.id}>
     247                                                    <TableCell sx={{maxWidth: 300}}>
     248                                                        <a
     249                                                            href={sug.link}
     250                                                            target="_blank"
     251                                                            rel="noopener noreferrer"
     252                                                            title={sug.link}
     253                                                            style={{textDecoration: 'none', color: 'inherit'}}
     254                                                        >
     255                                                            {sug.description || sug.link}
     256                                                        </a>
     257                                                    </TableCell>
     258                                                    <TableCell>{sug.componentType?.toUpperCase()}</TableCell>
     259                                                    <TableCell>{sug.userId}</TableCell>
     260                                                    <TableCell>
     261                                                        <Chip
     262                                                            label={sug.status || 'pending'}
     263                                                            size="small"
     264                                                            color={
     265                                                                sug.status === 'approved' ? 'success' :
     266                                                                    sug.status === 'rejected' ? 'error' :
     267                                                                        'default'
     268                                                            }
     269                                                        />
     270                                                    </TableCell>
     271                                                    <TableCell align="right">
     272                                                        {sug.status === 'pending' ? (
     273                                                            <>
     274                                                                <IconButton
     275                                                                    size="small"
     276                                                                    color="success"
     277                                                                    onClick={() => openSuggestionReview(sug.id, 'approved')}
     278                                                                >
     279                                                                    <CheckCircleIcon/>
     280                                                                </IconButton>
     281                                                                <IconButton
     282                                                                    size="small"
     283                                                                    color="error"
     284                                                                    onClick={() => openSuggestionReview(sug.id, 'rejected')}
     285                                                                >
     286                                                                    <CancelIcon/>
     287                                                                </IconButton>
     288                                                            </>
     289                                                        ) : sug.status === 'approved' ? (
     290                                                            <Button
     291                                                                size="small"
     292                                                                variant="contained"
     293                                                                color="warning"
     294                                                                startIcon={<AddIcon/>}
     295                                                                onClick={() => setCreateComponentDialog({
     296                                                                    open: true,
     297                                                                    suggestion: sug
     298                                                                })}
     299                                                            >
     300                                                                Create
     301                                                            </Button>
     302                                                        ) : (
     303                                                            <Typography variant="caption" color="text.secondary">
     304                                                                {sug.adminComment || 'Rejected'}
     305                                                            </Typography>
     306                                                        )}
     307                                                    </TableCell>
     308                                                </TableRow>
     309                                            ))}
     310                                        </TableBody>
     311                                    </Table>
     312                                )}
     313                            </Paper>
     314                        </Grid>
     315
     316                        <Grid item xs={12}>
     317                            <Paper sx={{p: 3, borderLeft: '6px solid #ed6c02', bgcolor: '#121212'}}>
     318                                <Box sx={{display: 'flex', alignItems: 'center', mb: 2}}>
     319                                    <BuildIcon color="warning" sx={{mr: 1}}/>
     320                                    <Typography variant="h6" fontWeight="bold">
     321                                        Builds Waiting for Approval ({data.pendingBuilds?.length || 0})
     322                                    </Typography>
     323                                </Box>
     324
     325                                {data.pendingBuilds?.length === 0 ? (
     326                                    <Typography color="text.secondary">No pending builds.</Typography>
     327                                ) : (
     328                                    <Grid container spacing={2}>
     329                                        {data.pendingBuilds.map((build: any) => (
     330                                            <Grid item xs={12} sm={6} md={4} lg={3} key={build.id}>
     331                                                <Box sx={{position: 'relative'}}>
     332                                                    <BuildCard
     333                                                        build={build}
     334                                                        onClick={() => setSelectedBuildId(build.id)}
    283335                                                    />
    284                                                 </TableCell>
    285                                                 <TableCell align="right" sx={{minWidth: 150}}>
    286                                                     {sug.status === 'pending' ? (
    287                                                         <>
    288                                                             <IconButton
    289                                                                 size="small"
    290                                                                 color="success"
    291                                                                 onClick={() => openSuggestionReview(sug.id, 'approved')}
    292                                                             >
    293                                                                 <CheckCircleIcon/>
    294                                                             </IconButton>
    295                                                             <IconButton
    296                                                                 size="small"
    297                                                                 color="error"
    298                                                                 onClick={() => openSuggestionReview(sug.id, 'rejected')}
    299                                                             >
    300                                                                 <CancelIcon/>
    301                                                             </IconButton>
    302                                                         </>
    303                                                     ) : sug.status === 'approved' ? (
     336                                                    <Box sx={{
     337                                                        mt: 1,
     338                                                        display: 'flex',
     339                                                        gap: 1,
     340                                                        justifyContent: 'center'
     341                                                    }}>
    304342                                                        <Button
    305                                                             size="small"
    306343                                                            variant="contained"
    307344                                                            color="warning"
    308                                                             startIcon={<AddIcon/>}
    309                                                             onClick={() => setCreateComponentDialog({
    310                                                                 open: true,
    311                                                                 suggestion: sug
    312                                                             })}
     345                                                            size="small"
     346                                                            startIcon={<BuildIcon/>}
     347                                                            onClick={(e) => {
     348                                                                e.stopPropagation();
     349                                                                openBuildApproval(build.id, build.name);
     350                                                            }}
    313351                                                        >
    314                                                             Create
     352                                                            Review
    315353                                                        </Button>
    316                                                     ) : (
    317                                                         <Typography variant="caption" color="text.secondary">
    318                                                             {sug.adminComment || 'Rejected'}
    319                                                         </Typography>
    320                                                     )}
    321                                                 </TableCell>
    322                                             </TableRow>
     354                                                    </Box>
     355                                                </Box>
     356                                            </Grid>
    323357                                        ))}
    324                                     </TableBody>
    325                                 </Table>
    326                             </Box>
    327                         )}
    328                     </Paper>
    329 
    330                     <Paper sx={{p: 3, borderLeft: '6px solid #ed6c02', bgcolor: '#121212'}}>
    331                         <Box sx={{display: 'flex', alignItems: 'center', mb: 2}}>
    332                             <BuildIcon color="warning" sx={{mr: 1}}/>
    333                             <Typography variant="h6" fontWeight="bold">
    334                                 Builds Waiting for Approval ({data.pendingBuilds?.length || 0})
    335                             </Typography>
    336                         </Box>
    337 
    338                         {data.pendingBuilds?.length === 0 ? (
    339                             <Typography color="text.secondary">No pending builds.</Typography>
    340                         ) : (
    341                             <Box sx={{
    342                                 display: 'grid',
    343                                 gridTemplateColumns: {
    344                                     xs: '1fr',
    345                                     sm: 'repeat(2, 1fr)',
    346                                     lg: 'repeat(3, 1fr)',
    347                                     xl: 'repeat(4, 1fr)'
    348                                 },
    349                                 gap: 2
    350                             }}>
    351                                 {data.pendingBuilds.map((build: any) => (
    352                                     <Box key={build.id}>
    353                                         <BuildCard
    354                                             build={build}
    355                                             onClick={() => setSelectedBuildId(build.id)}
    356                                         />
    357                                         <Box sx={{mt: 1, display: 'flex', gap: 1, justifyContent: 'center'}}>
    358                                             <Button
    359                                                 variant="contained"
    360                                                 color="warning"
    361                                                 size="small"
    362                                                 startIcon={<BuildIcon/>}
    363                                                 onClick={(e) => {
    364                                                     e.stopPropagation();
    365                                                     openBuildApproval(build.id, build.name);
    366                                                 }}
    367                                             >
    368                                                 Review
    369                                             </Button>
    370                                         </Box>
    371                                     </Box>
    372                                 ))}
    373                             </Box>
    374                         )}
    375                     </Paper>
    376 
    377                     <Paper sx={{p: 3, borderLeft: '6px solid #2e7d32'}}>
    378                         <Typography variant="h6" fontWeight="bold" gutterBottom>
    379                             My Builds / Sandbox
    380                         </Typography>
    381                         {data.userBuilds?.length === 0 ? (
    382                             <Typography color="text.secondary">No builds created by admin.</Typography>
    383                         ) : (
    384                             <Box sx={{
    385                                 display: 'grid',
    386                                 gridTemplateColumns: {
    387                                     xs: '1fr',
    388                                     sm: 'repeat(2, 1fr)',
    389                                     lg: 'repeat(3, 1fr)',
    390                                     xl: 'repeat(4, 1fr)'
    391                                 },
    392                                 gap: 2
    393                             }}>
    394                                 {data.userBuilds.slice(0, 4).map((build: any) => (
    395                                     <Box key={build.id}>
    396                                         <BuildCard
    397                                             build={build}
    398                                             onClick={() => setSelectedBuildId(build.id)}
    399                                         />
    400                                         <Box sx={{mt: 1, display: 'flex', justifyContent: 'center'}}>
    401                                             <Button
    402                                                 variant="outlined"
    403                                                 color="error"
    404                                                 size="small"
    405                                                 startIcon={<DeleteIcon/>}
    406                                                 onClick={(e) => {
    407                                                     e.stopPropagation();
    408                                                     openDeleteDialog(build.id, build.name);
    409                                                 }}
    410                                                 fullWidth
    411                                             >
    412                                                 Delete
    413                                             </Button>
    414                                         </Box>
    415                                     </Box>
    416                                 ))}
    417                             </Box>
    418                         )}
    419                     </Paper>
    420                 </Box>
    421             </Box>
     358                                    </Grid>
     359                                )}
     360                            </Paper>
     361                        </Grid>
     362
     363                        <Grid item xs={12}>
     364                            <Paper sx={{p: 3, borderLeft: '6px solid #2e7d32'}}>
     365                                <Typography variant="h6" fontWeight="bold" gutterBottom>
     366                                    My Builds / Sandbox
     367                                </Typography>
     368                                {data.userBuilds?.length === 0 ? (
     369                                    <Typography color="text.secondary">No builds created by admin.</Typography>
     370                                ) : (
     371                                    <Grid container spacing={2}>
     372                                        {data.userBuilds.slice(0, 4).map((build: any) => (
     373                                            <Grid item xs={12} sm={6} md={4} lg={3} key={build.id}>
     374                                                <Box sx={{position: 'relative'}}>
     375                                                    <BuildCard
     376                                                        build={build}
     377                                                        onClick={() => setSelectedBuildId(build.id)}
     378                                                    />
     379                                                    <Box sx={{mt: 1, display: 'flex', justifyContent: 'center'}}>
     380                                                        <Button
     381                                                            variant="outlined"
     382                                                            color="error"
     383                                                            size="small"
     384                                                            startIcon={<DeleteIcon/>}
     385                                                            onClick={(e) => {
     386                                                                e.stopPropagation();
     387                                                                openDeleteDialog(build.id, build.name);
     388                                                            }}
     389                                                            fullWidth
     390                                                        >
     391                                                            Delete
     392                                                        </Button>
     393                                                    </Box>
     394                                                </Box>
     395                                            </Grid>
     396                                        ))}
     397                                    </Grid>
     398                                )}
     399                            </Paper>
     400                        </Grid>
     401                    </Grid>
     402                </Grid>
     403            </Grid>
    422404
    423405            <Dialog open={suggestionDialog.open}
     
    538520            <AddComponentDialog
    539521                open={createComponentDialog.open}
    540                 onClose={() => setCreateComponentDialog({open: false, suggestion: null})}
     522                onClose={() => setCreateComponentDialog({ open: false, suggestion: null })}
    541523                onSuccess={() => {
    542524                    loadData();
    543                     setCreateComponentDialog({open: false, suggestion: null});
     525                    setCreateComponentDialog({ open: false, suggestion: null });
    544526                }}
    545527                prefillData={createComponentDialog.suggestion ? {
  • pages/dashboard/user/+Page.tsx

    r92fafb3 rc586cbd  
    1010    CircularProgress,
    1111    IconButton,
     12    Grid,
    1213    Dialog,
    1314    DialogTitle,
     
    2324
    2425import {getUserInfoAndData, onDeleteBuild} from "./userDashboard.telefunc";
     26import {onCloneBuild} from "../../+Layout.telefunc";
    2527
    2628import BuildCard from "../../../components/BuildCard";
     
    7072            await onDeleteBuild({buildId: deleteDialog.buildId});
    7173            setDeleteDialog({ open: false, buildId: null, buildName: '' });
    72             loadData();
     74            loadData(); // refresh na user i favorite builds
    7375            setSelectedBuildId(null);
    7476        } catch (e) {
     
    7981    };
    8082
     83    const handleCloneWrapper = async (buildId: number) => {
     84        if (!data?.user) return alert("Please login to clone builds!");
     85        if (confirm(`Clone this build to your dashboard?`)) {
     86            await onCloneBuild({buildId});
     87            alert("Build cloned! Check your dashboard.");
     88            setSelectedBuildId(null);
     89            loadData();
     90        }
     91    };
     92
    8193    if (loading) return <Box sx={{display: 'flex', justifyContent: 'center', mt: 10}}><CircularProgress/></Box>;
    82     if (error || !data) return <Container sx={{mt: 5, textAlign: 'center'}}><Typography color="error" variant="h6">{error}</Typography><Button href="/auth/login" variant="contained">Login</Button></Container>;
     94    if (error || !data) return <Container sx={{mt: 5, textAlign: 'center'}}><Typography color="error"
     95                                                                                        variant="h6">{error}</Typography><Button
     96        href="/auth/login" variant="contained">Login</Button></Container>;
    8397
    8498    return (
    85         <Container maxWidth="xl" sx={{mb: 4, color: 'white'}}>
    86             <Box sx={{
    87                 display: 'grid',
    88                 gridTemplateColumns: {
    89                     xs: '1fr',
    90                     md: '280px 1fr'
    91                 },
    92                 gap: 2
    93             }}>
    94                 <Box>
    95                     <Paper elevation={3} sx={{p: 3, display: 'flex', flexDirection: 'column', alignItems: 'center', height: '100%'}}>
    96                         <Avatar sx={{width: 100, height: 100, mb: 2, bgcolor: 'primary.main'}}>
    97                             <PersonIcon sx={{fontSize: 60}}/>
    98                         </Avatar>
     99        <Container maxWidth="xl" sx={{mt: 4, mb: 4, color: 'white'}}>
     100            <Grid container spacing={3}>
     101                <Grid item xs={12} md={3}>
     102                    <Paper elevation={3}
     103                           sx={{p: 3, display: 'flex', flexDirection: 'column', alignItems: 'center', height: '100%'}}>
     104                        <Avatar sx={{width: 100, height: 100, mb: 2, bgcolor: 'primary.main'}}><PersonIcon
     105                            sx={{fontSize: 60}}/></Avatar>
    99106                        <Typography variant="h5" fontWeight="bold" gutterBottom>{data.user.username}</Typography>
    100107                        <Typography variant="body2" color="text.secondary" gutterBottom>{data.user.email}</Typography>
    101108                        <Divider sx={{width: '100%', my: 2}}/>
    102109                    </Paper>
    103                 </Box>
    104 
    105                 <Box sx={{
    106                     display: 'flex',
    107                     flexDirection: 'column',
    108                     gap: 3
    109                 }}>
    110                     <Paper elevation={3} sx={{p: 3, minHeight: '300px', bgcolor: '#1e1e1e'}}>
    111                         <Box sx={{
    112                             display: 'flex',
    113                             alignItems: 'center',
    114                             justifyContent: 'space-between',
    115                             mb: 2
    116                         }}>
    117                             <Box sx={{display: 'flex', alignItems: 'center'}}>
    118                                 <FavoriteIcon color="error" sx={{mr: 1}}/>
    119                                 <Typography variant="h6" fontWeight="bold">Favorited Builds</Typography>
    120                             </Box>
    121 
    122                             {data.favoriteBuilds.length > 6 && (
    123                                 <Button
    124                                     variant="outlined"
    125                                     size="small"
    126                                     onClick={() => setOpenFavoritesDialog(true)}
    127                                 >
    128                                     See All ({data.favoriteBuilds.length})
    129                                 </Button>
    130                             )}
    131                         </Box>
    132                         <Divider sx={{mb: 2}}/>
    133 
    134                         {data.favoriteBuilds.length === 0 ? (
    135                             <Typography color="text.secondary" align="center" sx={{mt: 4}}>
    136                                 You haven't favorited any builds yet.
    137                             </Typography>
    138                         ) : (
    139                             <Box sx={{
    140                                 display: 'grid',
    141                                 gridTemplateColumns: {
    142                                     xs: '1fr',
    143                                     sm: 'repeat(2, 1fr)',
    144                                     md: 'repeat(3, 1fr)',
    145                                     lg: 'repeat(4, 1fr)',
    146                                     xl: 'repeat(6, 1fr)'
    147                                 },
    148                                 gap: 2
    149                             }}>
    150                                 {data.favoriteBuilds.slice(0, 6).map((build: any) => (
    151                                     <BuildCard
    152                                         key={build.id}
    153                                         build={build}
    154                                         onClick={() => setSelectedBuildId(build.id)}
    155                                     />
    156                                 ))}
    157                             </Box>
    158                         )}
    159                     </Paper>
    160 
    161                     <Paper elevation={3} sx={{p: 3, minHeight: '300px', bgcolor: '#1e1e1e'}}>
    162                         <Box sx={{
    163                             display: 'flex',
    164                             alignItems: 'center',
    165                             justifyContent: 'space-between',
    166                             mb: 2
    167                         }}>
    168                             <Box sx={{display: 'flex', alignItems: 'center'}}>
    169                                 <ComputerIcon color="primary" sx={{mr: 1}}/>
    170                                 <Typography variant="h6" fontWeight="bold">My Builds</Typography>
    171                             </Box>
    172 
    173                             {data.userBuilds.length > 6 && (
    174                                 <Button
    175                                     variant="outlined"
    176                                     size="small"
    177                                     onClick={() => setOpenMyBuildsDialog(true)}
    178                                 >
    179                                     See All ({data.userBuilds.length})
    180                                 </Button>
    181                             )}
    182                         </Box>
    183                         <Divider sx={{mb: 2}}/>
    184 
    185                         {data.userBuilds.length === 0 ? (
    186                             <Box sx={{textAlign: 'center', mt: 4}}>
    187                                 <Typography color="text.secondary" gutterBottom>
    188                                     You haven't created any builds yet.
    189                                 </Typography>
    190                                 <Button variant="contained" href="/forge" sx={{color: 'white', fontWeight: 'bolder'}}>
    191                                     Start Forging
    192                                 </Button>
    193                             </Box>
    194                         ) : (
    195                             <Box sx={{
    196                                 display: 'grid',
    197                                 gridTemplateColumns: {
    198                                     xs: '1fr',
    199                                     sm: 'repeat(2, 1fr)',
    200                                     md: 'repeat(3, 1fr)',
    201                                     lg: 'repeat(4, 1fr)',
    202                                     xl: 'repeat(6, 1fr)'
    203                                 },
    204                                 gap: 2
    205                             }}>
    206                                 {data.userBuilds.slice(0, 6).map((build: any) => (
    207                                     <Box key={build.id} sx={{ display: 'flex', flexDirection: 'column' }}>
    208                                         <Box sx={{ flexGrow: 1 }}>
     110                </Grid>
     111
     112                <Grid item xs={12} md={9}>
     113                    <Grid container spacing={3} direction="column">
     114
     115                        <Grid item xs={12}>
     116                            <Paper elevation={3} sx={{p: 3, minHeight: '300px', bgcolor: '#1e1e1e'}}>
     117                                <Box sx={{
     118                                    display: 'flex',
     119                                    alignItems: 'center',
     120                                    justifyContent: 'space-between',
     121                                    mb: 2
     122                                }}>
     123                                    <Box sx={{display: 'flex', alignItems: 'center'}}>
     124                                        <FavoriteIcon color="error" sx={{mr: 1}}/>
     125                                        <Typography variant="h6" fontWeight="bold">Favorited Builds</Typography>
     126                                    </Box>
     127
     128                                    {data.favoriteBuilds.length > 4 && (
     129                                        <Button
     130                                            variant="outlined"
     131                                            size="small"
     132                                            onClick={() => setOpenFavoritesDialog(true)}
     133                                        >
     134                                            See All ({data.favoriteBuilds.length})
     135                                        </Button>
     136                                    )}
     137                                </Box>
     138                                <Divider sx={{mb: 2}}/>
     139
     140                                {data.favoriteBuilds.length === 0 ? (
     141                                    <Typography color="text.secondary" align="center" sx={{mt: 4}}>You haven't favorited
     142                                        any builds yet.</Typography>
     143                                ) : (
     144                                    <Grid container spacing={2}>
     145                                        {data.favoriteBuilds.slice(0, 4).map((build: any) => (
     146                                            <Grid item xs={12} sm={6} md={4} key={build.id}>
     147                                                <BuildCard
     148                                                    build={build}
     149                                                    onClick={() => setSelectedBuildId(build.id)}
     150                                                />
     151                                            </Grid>
     152                                        ))}
     153                                    </Grid>
     154                                )}
     155                            </Paper>
     156                        </Grid>
     157
     158                        <Grid item xs={12}>
     159                            <Paper elevation={3} sx={{p: 3, minHeight: '300px', bgcolor: '#1e1e1e'}}>
     160                                <Box sx={{
     161                                    display: 'flex',
     162                                    alignItems: 'center',
     163                                    justifyContent: 'space-between',
     164                                    mb: 2
     165                                }}>
     166                                    <Box sx={{display: 'flex', alignItems: 'center'}}>
     167                                        <ComputerIcon color="primary" sx={{mr: 1}}/>
     168                                        <Typography variant="h6" fontWeight="bold">My Builds</Typography>
     169                                    </Box>
     170
     171                                    {data.userBuilds.length > 4 && (
     172                                        <Button
     173                                            variant="outlined"
     174                                            size="small"
     175                                            onClick={() => setOpenMyBuildsDialog(true)}
     176                                        >
     177                                            See All ({data.userBuilds.length})
     178                                        </Button>
     179                                    )}
     180                                </Box>
     181                                <Divider sx={{mb: 2}}/>
     182
     183                                {data.userBuilds.length === 0 ? (
     184                                    <Box sx={{textAlign: 'center', mt: 4}}>
     185                                        <Typography color="text.secondary" gutterBottom>You haven't created any builds
     186                                            yet.</Typography>
     187                                        <Button variant="contained" href="/forge"
     188                                                sx={{color: 'white', fontWeight: 'bolder'}}>Start Forging</Button>
     189                                    </Box>
     190                                ) : (
     191                                    <Grid container spacing={2}>
     192                                        {data.userBuilds.slice(0, 4).map((build: any) => (
     193                                            <Grid item xs={12} sm={6} md={4} key={build.id}>
     194                                                <Box sx={{ position: 'relative' }}>
     195                                                    <BuildCard
     196                                                        build={build}
     197                                                        onClick={() => setSelectedBuildId(build.id)}
     198                                                    />
     199                                                    <Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>
     200                                                        <Button
     201                                                            variant="outlined"
     202                                                            color="error"
     203                                                            size="small"
     204                                                            startIcon={<DeleteIcon />}
     205                                                            onClick={(e) => {
     206                                                                e.stopPropagation();
     207                                                                openDeleteDialog(build.id, build.name);
     208                                                            }}
     209                                                            sx={{ width: '100%' }}
     210                                                        >
     211                                                            Delete
     212                                                        </Button>
     213                                                    </Box>
     214                                                </Box>
     215                                            </Grid>
     216                                        ))}
     217                                    </Grid>
     218                                )}
     219                            </Paper>
     220                        </Grid>
     221
     222                        <Dialog open={openMyBuildsDialog} onClose={() => setOpenMyBuildsDialog(false)} maxWidth="lg"
     223                                fullWidth>
     224                            <DialogTitle sx={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
     225                                All My Builds
     226                                <IconButton onClick={() => setOpenMyBuildsDialog(false)}>
     227                                    <CloseIcon/>
     228                                </IconButton>
     229                            </DialogTitle>
     230                            <DialogContent dividers>
     231                                <Grid container spacing={2} sx={{mt: 1}}>
     232                                    {data.userBuilds.map((build: any) => (
     233                                        <Grid item xs={12} sm={6} md={4} key={build.id}>
     234                                            <Box sx={{ position: 'relative' }}>
     235                                                <BuildCard
     236                                                    build={build}
     237                                                    onClick={() => {
     238                                                        setOpenMyBuildsDialog(false);
     239                                                        setSelectedBuildId(build.id);
     240                                                    }}
     241                                                />
     242                                                <Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>
     243                                                    <Button
     244                                                        variant="outlined"
     245                                                        color="error"
     246                                                        size="small"
     247                                                        startIcon={<DeleteIcon />}
     248                                                        onClick={(e) => {
     249                                                            e.stopPropagation();
     250                                                            openDeleteDialog(build.id, build.name);
     251                                                        }}
     252                                                        sx={{ width: '100%' }}
     253                                                    >
     254                                                        Delete
     255                                                    </Button>
     256                                                </Box>
     257                                            </Box>
     258                                        </Grid>
     259                                    ))}
     260                                </Grid>
     261                            </DialogContent>
     262                            <DialogActions>
     263                                <Button onClick={() => setOpenMyBuildsDialog(false)}>Close</Button>
     264                            </DialogActions>
     265                        </Dialog>
     266
     267                        <Dialog open={openFavoritesDialog} onClose={() => setOpenFavoritesDialog(false)} maxWidth="lg"
     268                                fullWidth>
     269                            <DialogTitle sx={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
     270                                All Favorited Builds
     271                                <IconButton onClick={() => setOpenFavoritesDialog(false)}>
     272                                    <CloseIcon/>
     273                                </IconButton>
     274                            </DialogTitle>
     275                            <DialogContent dividers>
     276                                <Grid container spacing={2} sx={{mt: 1}}>
     277                                    {data.favoriteBuilds.map((build: any) => (
     278                                        <Grid item xs={12} sm={6} md={4} key={build.id}>
    209279                                            <BuildCard
    210280                                                build={build}
    211                                                 onClick={() => setSelectedBuildId(build.id)}
     281                                                onClick={() => {
     282                                                    setOpenFavoritesDialog(false);
     283                                                    setSelectedBuildId(build.id);
     284                                                }}
    212285                                            />
    213                                         </Box>
    214                                         <Button
    215                                             variant="outlined"
    216                                             color="error"
    217                                             size="small"
    218                                             startIcon={<DeleteIcon/>}
    219                                             onClick={(e) => {
    220                                                 e.stopPropagation();
    221                                                 openDeleteDialog(build.id, build.name);
    222                                             }}
    223                                             sx={{ mt: 1, width: '100%' }}
    224                                         >
    225                                             Delete
    226                                         </Button>
    227                                     </Box>
    228                                 ))}
    229                             </Box>
    230                         )}
    231                     </Paper>
    232                 </Box>
    233             </Box>
    234 
    235             <Dialog open={openMyBuildsDialog} onClose={() => setOpenMyBuildsDialog(false)} maxWidth="xl" fullWidth>
    236                 <DialogTitle sx={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
    237                     All My Builds
    238                     <IconButton onClick={() => setOpenMyBuildsDialog(false)}>
    239                         <CloseIcon/>
    240                     </IconButton>
    241                 </DialogTitle>
    242                 <DialogContent dividers>
    243                     <Box sx={{
    244                         display: 'grid',
    245                         gridTemplateColumns: {
    246                             xs: '1fr',
    247                             sm: 'repeat(2, 1fr)',
    248                             md: 'repeat(3, 1fr)',
    249                             lg: 'repeat(4, 1fr)',
    250                             xl: 'repeat(5, 1fr)'
    251                         },
    252                         gap: 2,
    253                         mt: 1
    254                     }}>
    255                         {data.userBuilds.map((build: any) => (
    256                             <Box key={build.id}>
    257                                 <BuildCard
    258                                     build={build}
    259                                     onClick={() => {
    260                                         setOpenMyBuildsDialog(false);
    261                                         setSelectedBuildId(build.id);
    262                                     }}
    263                                 />
    264                                 <Box sx={{mt: 1, display: 'flex', justifyContent: 'center'}}>
    265                                     <Button
    266                                         variant="outlined"
    267                                         color="error"
    268                                         size="small"
    269                                         startIcon={<DeleteIcon/>}
    270                                         onClick={(e) => {
    271                                             e.stopPropagation();
    272                                             openDeleteDialog(build.id, build.name);
    273                                         }}
    274                                         sx={{width: '100%'}}
    275                                     >
    276                                         Delete
    277                                     </Button>
    278                                 </Box>
    279                             </Box>
    280                         ))}
    281                     </Box>
    282                 </DialogContent>
    283                 <DialogActions>
    284                     <Button onClick={() => setOpenMyBuildsDialog(false)}>Close</Button>
    285                 </DialogActions>
    286             </Dialog>
    287 
    288             <Dialog open={openFavoritesDialog} onClose={() => setOpenFavoritesDialog(false)} maxWidth="xl" fullWidth>
    289                 <DialogTitle sx={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
    290                     All Favorited Builds
    291                     <IconButton onClick={() => setOpenFavoritesDialog(false)}>
    292                         <CloseIcon/>
    293                     </IconButton>
    294                 </DialogTitle>
    295                 <DialogContent dividers>
    296                     <Box sx={{
    297                         display: 'grid',
    298                         gridTemplateColumns: {
    299                             xs: '1fr',
    300                             sm: 'repeat(2, 1fr)',
    301                             md: 'repeat(3, 1fr)',
    302                             lg: 'repeat(4, 1fr)',
    303                             xl: 'repeat(5, 1fr)'
    304                         },
    305                         gap: 2,
    306                         mt: 1
    307                     }}>
    308                         {data.favoriteBuilds.map((build: any) => (
    309                             <BuildCard
    310                                 key={build.id}
    311                                 build={build}
    312                                 onClick={() => {
    313                                     setOpenFavoritesDialog(false);
    314                                     setSelectedBuildId(build.id);
    315                                 }}
    316                             />
    317                         ))}
    318                     </Box>
    319                 </DialogContent>
    320                 <DialogActions>
    321                     <Button onClick={() => setOpenFavoritesDialog(false)}>Close</Button>
    322                 </DialogActions>
    323             </Dialog>
    324 
    325             <Dialog open={deleteDialog.open} onClose={() => setDeleteDialog({ open: false, buildId: null, buildName: '' })}>
    326                 <DialogTitle>Delete Build</DialogTitle>
    327                 <DialogContent>
    328                     <Typography variant="h6" gutterBottom>{deleteDialog.buildName}</Typography>
    329                     <Typography color="text.secondary">
    330                         Are you sure you want to permanently delete this build? This action cannot be undone.
    331                     </Typography>
    332                 </DialogContent>
    333                 <DialogActions>
    334                     <Button
    335                         onClick={() => setDeleteDialog({ open: false, buildId: null, buildName: '' })}
    336                         disabled={deleteLoading}
    337                     >
    338                         Cancel
    339                     </Button>
    340                     <Button
    341                         onClick={handleDelete}
    342                         variant="contained"
    343                         color="error"
    344                         disabled={deleteLoading}
    345                         startIcon={deleteLoading ? <CircularProgress size={20} /> : <DeleteIcon />}
    346                     >
    347                         {deleteLoading ? 'Deleting...' : 'Delete Build'}
    348                     </Button>
    349                 </DialogActions>
    350             </Dialog>
     286                                        </Grid>
     287                                    ))}
     288                                </Grid>
     289                            </DialogContent>
     290                            <DialogActions>
     291                                <Button onClick={() => setOpenFavoritesDialog(false)}>Close</Button>
     292                            </DialogActions>
     293                        </Dialog>
     294
     295                        <Dialog open={deleteDialog.open} onClose={() => setDeleteDialog({ open: false, buildId: null, buildName: '' })}>
     296                            <DialogTitle>Delete Build</DialogTitle>
     297                            <DialogContent>
     298                                <Typography variant="h6" gutterBottom>{deleteDialog.buildName}</Typography>
     299                                <Typography color="text.secondary">
     300                                    Are you sure you want to permanently delete this build? This action cannot be undone.
     301                                </Typography>
     302                            </DialogContent>
     303                            <DialogActions>
     304                                <Button
     305                                    onClick={() => setDeleteDialog({ open: false, buildId: null, buildName: '' })}
     306                                    disabled={deleteLoading}
     307                                >
     308                                    Cancel
     309                                </Button>
     310                                <Button
     311                                    onClick={handleDelete}
     312                                    variant="contained"
     313                                    color="error"
     314                                    disabled={deleteLoading}
     315                                    startIcon={deleteLoading ? <CircularProgress size={20} /> : <DeleteIcon />}
     316                                >
     317                                    {deleteLoading ? 'Deleting...' : 'Delete Build'}
     318                                </Button>
     319                            </DialogActions>
     320                        </Dialog>
     321
     322                    </Grid>
     323                </Grid>
     324            </Grid>
    351325
    352326            <BuildDetailsDialog
     
    358332                    loadData();
    359333                }}
     334                onClone={handleCloneWrapper}
    360335                isDashboardView={true}
    361336            />
  • pages/index/+Page.tsx

    r92fafb3 rc586cbd  
    1010import BuildCard from '../../components/BuildCard';
    1111
    12 import {onGetApprovedBuilds, onGetAuthState} from '../+Layout.telefunc';
     12import {onGetApprovedBuilds, onGetAuthState, onCloneBuild} from '../+Layout.telefunc';
    1313
    1414export default function HomePage() {
     
    4646        }
    4747
    48         void loadSite();
     48        loadSite();
    4949    }, []);
     50
     51    const handleCloneWrapper = async (buildId: number) => {
     52        if (!data?.isLoggedIn) return alert("Please login to clone builds!");
     53        if (confirm(`Clone this build to your dashboard?`)) {
     54            await onCloneBuild({buildId});
     55            alert("Build cloned! Check your dashboard.");
     56            setSelectedBuildId(null);
     57        }
     58    };
    5059
    5160    if (!data) return <Box sx={{p: 10, textAlign: 'center'}}>Loading Forge...</Box>;
     
    151160                currentUser={data.userId}
    152161                onClose={() => setSelectedBuildId(null)}
     162                onClone={handleCloneWrapper}
    153163            />
    154164
Note: See TracChangeset for help on using the changeset viewer.