Changes in / [dda6f51:f46bf5c]


Ignore:
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • components/BuildDetailsDialog.tsx

    rdda6f51 rf46bf5c  
    1010import PersonIcon from "@mui/icons-material/Person";
    1111import {onGetBuildDetails, onSetReview, onToggleFavorite, onCloneBuild, onSetRating} from '../pages/+Layout.telefunc';
    12 
    13 const formatPrice = (price: any) => new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(Number(price) || 0);
    14 
    15 export default function BuildDetailsDialog({ open, buildId, onClose, currentUser }: any) {
     12import {onGetBuildState} from '../pages/forge/forge.telefunc';
     13
     14const formatPrice = (price: any) => new Intl.NumberFormat('en-US', {
     15    style: 'currency',
     16    currency: 'USD'
     17}).format(Number(price) || 0);
     18
     19export default function BuildDetailsDialog({open, buildId, onClose, currentUser, isDashboardView = false}: {
     20    open: boolean;
     21    buildId: number | null;
     22    onClose: () => void;
     23    currentUser: any;
     24    isDashboardView?: boolean;
     25}) {
    1626    const [details, setDetails] = useState<any>(null);
    1727    const [loading, setLoading] = useState(false);
     
    1929    const [cloneDialogOpen, setCloneDialogOpen] = useState(false);
    2030    const [cloningBuildId, setCloningBuildId] = useState<number | null>(null);
     31    const [isOwner, setIsOwner] = useState(false);
    2132
    2233    const [reviewText, setReviewText] = useState("");
    2334    const [ratingVal, setRatingVal] = useState(5);
    2435
     36    // Main details fetch
    2537    useEffect(() => {
    26         if (open && buildId) {
     38        if (open && buildId !== null && typeof buildId === 'number') {
    2739            setLoading(true);
    2840            setReviewText("");
    2941            setRatingVal(5);
    3042
    31             onGetBuildDetails({ buildId })
     43            onGetBuildDetails({buildId})
    3244                .then(data => {
    3345                    setDetails(data);
     
    3951    }, [open, buildId]);
    4052
     53    // Ownership check for edit button
     54    useEffect(() => {
     55        if (open && buildId !== null && typeof buildId === 'number') {
     56            onGetBuildState({ buildId })  // ← Only buildId, no userId!
     57                .then(state => {
     58                    setIsOwner(!!state);
     59                })
     60                .catch(() => setIsOwner(false));
     61        } else {
     62            setIsOwner(false);
     63        }
     64    }, [open, buildId]);
     65
    4166    const handleFavorite = async () => {
    42         if (!currentUser) return alert("Please login to favorite builds.");
    43         const res = await onToggleFavorite({ buildId });
    44         setDetails((prev: any) => ({ ...prev, isFavorite: res }));
     67        if (!currentUser || buildId === null) return alert("Please login to favorite builds.");
     68        const res = await onToggleFavorite({buildId});
     69        setDetails((prev: any) => ({...prev, isFavorite: res}));
    4570    };
    4671
    4772    const handleSubmitReview = async () => {
    48         if (!currentUser) return alert("Please login to review.");
     73        if (!currentUser || buildId === null) return alert("Please login to review.");
    4974
    5075        await onSetReview({
    5176            buildId,
    5277            content: reviewText,
    53             // rating: ratingVal
    5478        });
    5579
     
    5781            buildId,
    5882            value: ratingVal
    59         })
    60 
    61         const refreshed = await onGetBuildDetails({ buildId });
     83        });
     84
     85        const refreshed = await onGetBuildDetails({buildId});
    6286        setDetails(refreshed);
    6387    };
     
    6892        try {
    6993            const newBuildId = await onCloneBuild({ buildId: cloningBuildId });
    70 
    7194            window.location.href = `/forge?buildId=${newBuildId}`;
    72 
    7395            setCloneDialogOpen(false);
    7496            setCloningBuildId(null);
     
    78100    };
    79101
    80 
    81102    if (!open) return null;
    82103
     
    85106            <Dialog open={open} onClose={onClose} maxWidth="md" fullWidth scroll="paper">
    86107                {loading || !details ? (
    87                     <Box sx={{ p: 5, textAlign: 'center' }}>Loading Forge Schematics...</Box>
     108                    <Box sx={{p: 5, textAlign: 'center'}}>Loading Forge Schematics...</Box>
    88109                ) : (
    89110                    <>
    90                         <DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', bgcolor: '#ff8201' }}>
     111                        <DialogTitle sx={{
     112                            display: 'flex',
     113                            justifyContent: 'space-between',
     114                            alignItems: 'center',
     115                            bgcolor: '#ff8201'
     116                        }}>
    91117                            <Box>
    92118                                <Typography variant="h5" fontWeight="bold">{details.name}</Typography>
    93                                 <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
    94                                     <PersonIcon sx={{ fontSize: 16 }} />
     119                                <Box sx={{display: 'flex', alignItems: 'center', gap: 1}}>
     120                                    <PersonIcon sx={{fontSize: 16}}/>
    95121                                    <Typography variant="subtitle2" color="text.secondary" fontWeight="bold">
    96122                                        by {details.creator}
    97123                                    </Typography>
    98                                     <Chip label={formatPrice(details.totalPrice)} size="small" color="primary" variant="outlined" />
     124                                    <Chip label={formatPrice(details.totalPrice)} size="small" color="primary"
     125                                          variant="outlined"/>
    99126                                </Box>
    100127                            </Box>
    101                             <IconButton onClick={onClose}><CloseIcon /></IconButton>
     128                            <IconButton onClick={onClose}><CloseIcon/></IconButton>
    102129                        </DialogTitle>
    103130
    104                         <DialogContent sx={{ p: 0 }}>
    105                             <Box sx={{ borderBottom: 1, borderColor: 'divider', px: 2, bgcolor: 'primary', position: 'sticky', top: 0, zIndex: 1 }}>
     131                        <DialogContent sx={{p: 0}}>
     132                            <Box sx={{
     133                                borderBottom: 1,
     134                                borderColor: 'divider',
     135                                px: 2,
     136                                bgcolor: 'primary',
     137                                position: 'sticky',
     138                                top: 0,
     139                                zIndex: 1
     140                            }}>
    106141                                <Tabs value={tabIndex} onChange={(_, v) => setTabIndex(v)}>
    107                                     <Tab label="Specs" />
    108                                     <Tab label={`Reviews (${details.ratingStatistics.ratingCount})`} />
     142                                    <Tab label="Specs"/>
     143                                    <Tab label={`Reviews (${details.ratingStatistics.ratingCount})`}/>
    109144                                </Tabs>
    110145                            </Box>
    111146
    112                             <Box sx={{ p: 3 }}>
     147                            <Box sx={{p: 3}}>
    113148                                {tabIndex === 0 && (
    114149                                    <Grid container spacing={2}>
     
    118153                                                    {details.components.map((comp: any) => (
    119154                                                        <TableRow key={comp.id}>
    120                                                             <TableCell sx={{ width: 50 }}>
     155                                                            <TableCell sx={{width: 50}}>
    121156                                                                <Avatar
    122157                                                                    src={comp.img_url || undefined}
    123158                                                                    variant="rounded"
    124                                                                     sx={{ width: 45, height: 45, bgcolor: '#ff8201'}}
     159                                                                    sx={{width: 45, height: 45, bgcolor: '#ff8201'}}
    125160                                                                >
    126161                                                                    {comp.type?.substring(0, 3)?.toUpperCase()}
     
    128163                                                            </TableCell>
    129164                                                            <TableCell>
    130                                                                 <Typography variant="body2" color="text.secondary" sx={{ fontSize: '0.75rem', textTransform: 'uppercase' }}>
     165                                                                <Typography variant="body2" color="text.secondary" sx={{
     166                                                                    fontSize: '0.75rem',
     167                                                                    textTransform: 'uppercase'
     168                                                                }}>
    131169                                                                    {comp.type}
    132170                                                                </Typography>
     
    135173                                                                </Typography>
    136174                                                            </TableCell>
    137                                                             <TableCell align="right" sx={{ fontWeight: 'bold', color: '#ff8201' }}>
     175                                                            <TableCell align="right"
     176                                                                       sx={{fontWeight: 'bold', color: '#ff8201'}}>
    138177                                                                {formatPrice(comp.price)}
    139178                                                            </TableCell>
    140179                                                        </TableRow>
    141180                                                    ))}
    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' }}>
     181                                                    <TableRow sx={{bgcolor: '#424343'}}>
     182                                                        <TableCell colSpan={2} sx={{
     183                                                            fontWeight: 'bold',
     184                                                            color: '#ff8201'
     185                                                        }}>TOTAL</TableCell>
     186                                                        <TableCell align="right" sx={{
     187                                                            fontWeight: 'bold',
     188                                                            fontSize: '1.1rem',
     189                                                            color: 'primary.main'
     190                                                        }}>
    145191                                                            {formatPrice(details.totalPrice)}
    146192                                                        </TableCell>
     
    151197
    152198                                        <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' }}>
     199                                            <Box sx={{bgcolor: '#424343', p: 2, borderRadius: 2, mb: 2}}>
     200                                                <Typography color="primary.main" gutterBottom fontWeight="bold">Builder's
     201                                                    Notes</Typography>
     202                                                <Typography color="primary.main" variant="body2"
     203                                                            sx={{fontStyle: 'italic'}}>
    156204                                                    "{details.description || "No notes provided."}"
    157205                                                </Typography>
    158206                                            </Box>
    159207
    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>
     208                                            <Box sx={{display: 'flex', flexDirection: 'column', gap: 1}}>
     209                                                {isDashboardView && isOwner ? (
     210                                                    <Button
     211                                                        variant="contained"
     212                                                        color="primary"
     213                                                        size="large"
     214                                                        startIcon={<AutoFixHighIcon/>}
     215                                                        onClick={() => {
     216                                                            window.location.href = `/forge?buildId=${details.id}`;
     217                                                            onClose();
     218                                                        }}
     219                                                    >
     220                                                        Edit Build
     221                                                    </Button>
     222                                                ) : (
     223                                                    <Button
     224                                                        variant="contained"
     225                                                        color="primary"
     226                                                        size="large"
     227                                                        startIcon={<AutoFixHighIcon/>}
     228                                                        onClick={() => {
     229                                                            setCloningBuildId(details.id);
     230                                                            setCloneDialogOpen(true);
     231                                                        }}
     232                                                    >
     233                                                        Clone & Edit
     234                                                    </Button>
     235                                                )}
    173236                                                <Button
    174237                                                    variant={details.isFavorite ? "contained" : "outlined"}
    175238                                                    color={details.isFavorite ? "error" : "primary"}
    176                                                     startIcon={details.isFavorite ? <FavoriteIcon /> : <FavoriteBorderIcon />}
     239                                                    startIcon={details.isFavorite ? <FavoriteIcon/> :
     240                                                        <FavoriteBorderIcon/>}
    177241                                                    onClick={handleFavorite}
    178242                                                >
     
    186250                                {tabIndex === 1 && (
    187251                                    <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>
     252                                        <Box sx={{
     253                                            display: 'flex',
     254                                            alignItems: 'center',
     255                                            gap: 2,
     256                                            mb: 4,
     257                                            p: 2,
     258                                            bgcolor: '#5e5e5e',
     259                                            borderRadius: 2
     260                                        }}>
     261                                            <Typography variant="h3"
     262                                                        fontWeight="bold">{details.ratingStatistics.averageRating.toFixed(1)}</Typography>
    190263                                            <Box>
    191                                                 <Rating value={details.ratingStatistics.averageRating} readOnly precision={0.5} />
    192                                                 <Typography variant="body2" color="text.secondary">{details.ratingStatistics.ratingCount} ratings</Typography>
     264                                                <Rating value={details.ratingStatistics.averageRating} readOnly
     265                                                        precision={0.5}/>
     266                                                <Typography variant="body2"
     267                                                            color="text.secondary">{details.ratingStatistics.ratingCount} ratings</Typography>
    193268                                            </Box>
    194269                                        </Box>
    195270
    196271                                        {currentUser && details.userId !== currentUser.id && (
    197                                             <Box sx={{ mb: 4, p: 2, border: '1px solid #ddd', borderRadius: 2 }}>
     272                                            <Box sx={{mb: 4, p: 2, border: '1px solid #ddd', borderRadius: 2}}>
    198273                                                <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)} />
     274                                                <Box sx={{display: 'flex', alignItems: 'center', mb: 1}}>
     275                                                    <Rating value={ratingVal}
     276                                                            onChange={(_, v) => setRatingVal(v || 5)}/>
    201277                                                </Box>
    202278                                                <TextField
     
    207283                                                    value={reviewText}
    208284                                                    onChange={(e) => setReviewText(e.target.value)}
    209                                                     sx={{ mb: 1 }}
     285                                                    sx={{mb: 1}}
    210286                                                />
    211287                                                <Button size="small" variant="contained" onClick={handleSubmitReview}>
     
    216292
    217293                                        {currentUser && details.userId === currentUser.id && (
    218                                             <Alert severity="info" sx={{ mb: 4 }}>
     294                                            <Alert severity="info" sx={{mb: 4}}>
    219295                                                You cannot rate your own builds.
    220296                                            </Alert>
    221297                                        )}
    222298
    223                                         <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
     299                                        <Box sx={{display: 'flex', flexDirection: 'column', gap: 2}}>
    224300                                            {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>
     301                                                <Box key={i} sx={{pb: 2, borderBottom: '1px solid #eee'}}>
     302                                                    <Box sx={{
     303                                                        display: 'flex',
     304                                                        justifyContent: 'space-between',
     305                                                        mb: 0.5
     306                                                    }}>
     307                                                        <Typography fontWeight="bold"
     308                                                                    variant="body2">{rev.username}</Typography>
     309                                                        <Typography variant="caption"
     310                                                                    color="text.secondary">{rev.createdAt}</Typography>
    229311                                                    </Box>
    230312                                                    <Typography variant="body2">{rev.content}</Typography>
     
    232314                                            ))}
    233315                                            {details.reviews.length === 0 && (
    234                                                 <Typography color="text.secondary" align="center">No reviews yet. Be the first!</Typography>
     316                                                <Typography color="text.secondary" align="center">No reviews yet. Be the
     317                                                    first!</Typography>
    235318                                            )}
    236319                                        </Box>
  • pages/dashboard/admin/+Page.tsx

    rdda6f51 rf46bf5c  
    1919import BuildCard from '../../../components/BuildCard';
    2020import BuildDetailsDialog from '../../../components/BuildDetailsDialog';
     21import DeleteIcon from "@mui/icons-material/Delete";
     22import {onDeleteBuild} from "../user/userDashboard.telefunc";
    2123
    2224export default function AdminDashboard() {
     
    2426    const [loading, setLoading] = useState(true);
    2527    const [selectedBuildId, setSelectedBuildId] = useState<number | null>(null);
    26 
    27     // Build approval dialog state
     28    const [deleteDialog, setDeleteDialog] = useState<{ open: boolean, buildId: number | null, buildName: string }>({ open: false, buildId: null, buildName: '' });
     29    const [deleteLoading, setDeleteLoading] = useState(false);
     30
    2831    const [buildApprovalDialog, setBuildApprovalDialog] = useState<{
    2932        open: boolean,
     
    107110        } catch (e) {
    108111            alert("Failed to update suggestion");
     112        }
     113    };
     114
     115    const openDeleteDialog = (buildId: number, buildName: string) => {
     116        setDeleteDialog({ open: true, buildId, buildName });
     117    };
     118
     119    const handleDelete = async () => {
     120        if (!deleteDialog.buildId) return;
     121        setDeleteLoading(true);
     122        try {
     123            await onDeleteBuild({buildId: deleteDialog.buildId});
     124            setDeleteDialog({ open: false, buildId: null, buildName: '' });
     125            loadData(); // refresh na user i favorite builds
     126            setSelectedBuildId(null);
     127        } catch (e) {
     128            alert("Failed to delete build");
     129        } finally {
     130            setDeleteLoading(false);
    109131        }
    110132    };
     
    253275                                    <Grid container spacing={2}>
    254276                                        {data.userBuilds.slice(0, 4).map((build: any) => (
    255                                             <Grid item xs={12} md={3} key={build.id}>
    256                                                 <BuildCard
    257                                                     build={build}
    258                                                     onClick={() => setSelectedBuildId(build.id)}
    259                                                 />
     277                                            <Grid item xs={12} sm={6} md={4} key={build.id}>
     278                                                <Box sx={{ position: 'relative' }}>
     279                                                    <BuildCard
     280                                                        build={build}
     281                                                        onClick={() => setSelectedBuildId(build.id)}
     282                                                    />
     283                                                    <Box sx={{ mt: 1, display: 'flex', justifyContent: 'center' }}>
     284                                                        <Button
     285                                                            variant="outlined"
     286                                                            color="error"
     287                                                            size="small"
     288                                                            startIcon={<DeleteIcon />}
     289                                                            onClick={(e) => {
     290                                                                e.stopPropagation();
     291                                                                openDeleteDialog(build.id, build.name);
     292                                                            }}
     293                                                            sx={{ width: '100%' }}
     294                                                        >
     295                                                            Delete
     296                                                        </Button>
     297                                                    </Box>
     298                                                </Box>
    260299                                            </Grid>
    261300                                        ))}
     
    329368            </Dialog>
    330369
     370            <Dialog open={deleteDialog.open} onClose={() => setDeleteDialog({ open: false, buildId: null, buildName: '' })}>
     371                <DialogTitle>Delete Build</DialogTitle>
     372                <DialogContent>
     373                    <Typography variant="h6" gutterBottom>{deleteDialog.buildName}</Typography>
     374                    <Typography color="text.secondary">
     375                        Are you sure you want to permanently delete this build? This action cannot be undone.
     376                    </Typography>
     377                </DialogContent>
     378                <DialogActions>
     379                    <Button
     380                        onClick={() => setDeleteDialog({ open: false, buildId: null, buildName: '' })}
     381                        disabled={deleteLoading}
     382                    >
     383                        Cancel
     384                    </Button>
     385                    <Button
     386                        onClick={handleDelete}
     387                        variant="contained"
     388                        color="error"
     389                        disabled={deleteLoading}
     390                        startIcon={deleteLoading ? <CircularProgress size={20} /> : <DeleteIcon />}
     391                    >
     392                        {deleteLoading ? 'Deleting...' : 'Delete Build'}
     393                    </Button>
     394                </DialogActions>
     395            </Dialog>
     396
    331397            <BuildDetailsDialog
    332398                open={!!selectedBuildId}
     
    335401                onClose={() => setSelectedBuildId(null)}
    336402                onClone={() => alert("Clone disabled in admin view")}
     403                isDashboardView={true}
    337404            />
    338405        </Container>
  • pages/dashboard/user/+Page.tsx

    rdda6f51 rf46bf5c  
    1717} from '@mui/material';
    1818import CloseIcon from '@mui/icons-material/Close';
     19import DeleteIcon from '@mui/icons-material/Delete';
    1920
    2021import PersonIcon from '@mui/icons-material/Person';
     
    3839    const [loading, setLoading] = useState(true);
    3940    const [error, setError] = useState<string | null>(null);
    40 
    4141    const [openMyBuildsDialog, setOpenMyBuildsDialog] = useState(false);
    4242    const [openFavoritesDialog, setOpenFavoritesDialog] = useState(false);
    43 
    4443    const [selectedBuildId, setSelectedBuildId] = useState<number | null>(null);
     44    const [deleteDialog, setDeleteDialog] = useState<{ open: boolean, buildId: number | null, buildName: string }>({ open: false, buildId: null, buildName: '' });
     45    const [deleteLoading, setDeleteLoading] = useState(false);
    4546
    4647    const loadData = () => {
     
    6162    }, []);
    6263
    63     const handleDelete = async (buildId: number) => {
    64         if (!confirm("Are you sure you want to delete this build?")) return;
     64    const openDeleteDialog = (buildId: number, buildName: string) => {
     65        setDeleteDialog({ open: true, buildId, buildName });
     66    };
     67
     68    const handleDelete = async () => {
     69        if (!deleteDialog.buildId) return;
     70        setDeleteLoading(true);
    6571        try {
    66             await onDeleteBuild({buildId});
    67             loadData();
     72            await onDeleteBuild({buildId: deleteDialog.buildId});
     73            setDeleteDialog({ open: false, buildId: null, buildName: '' });
     74            loadData(); // refresh na user i favorite builds
    6875            setSelectedBuildId(null);
    6976        } catch (e) {
    7077            alert("Failed to delete build");
     78        } finally {
     79            setDeleteLoading(false);
    7180        }
    7281    };
     
    8695                                                                                        variant="h6">{error}</Typography><Button
    8796        href="/auth/login" variant="contained">Login</Button></Container>;
    88     // console.log("Current User ID passed to dialog:", data?.user?.id);
    8997
    9098    return (
     
    150158                        <Grid item xs={12}>
    151159                            <Paper elevation={3} sx={{p: 3, minHeight: '300px', bgcolor: '#1e1e1e'}}>
    152 
    153160                                <Box sx={{
    154161                                    display: 'flex',
     
    185192                                        {data.userBuilds.slice(0, 4).map((build: any) => (
    186193                                            <Grid item xs={12} sm={6} md={4} key={build.id}>
    187                                                 <BuildCard
    188                                                     build={build}
    189                                                     onClick={() => setSelectedBuildId(build.id)}
    190                                                 />
     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>
    191215                                            </Grid>
    192216                                        ))}
     
    208232                                    {data.userBuilds.map((build: any) => (
    209233                                        <Grid item xs={12} sm={6} md={4} key={build.id}>
    210                                             <BuildCard
    211                                                 build={build}
    212                                                 onClick={() => {
    213                                                     setOpenMyBuildsDialog(false);
    214                                                     setSelectedBuildId(build.id);
    215                                                 }}
    216                                             />
     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>
    217258                                        </Grid>
    218259                                    ))}
     
    252293                        </Dialog>
    253294
     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
    254322                    </Grid>
    255323                </Grid>
     
    265333                }}
    266334                onClone={handleCloneWrapper}
     335                isDashboardView={true}
    267336            />
    268337        </Container>
  • pages/forge/+Page.tsx

    rdda6f51 rf46bf5c  
    131131            let id = buildId;
    132132            if (!id) {
    133                 const result = await onAddNewBuild({name: "Draft Build", description: "Work in progress"});
     133                const result = await onAddNewBuild({
     134                    name: buildName.trim() || "New Build",
     135                    description: description || "Work in progress"
     136                });
    134137                id = typeof result === 'number' ? result : (result as any)?.buildId;
    135138                if (!id || !Number.isInteger(id) || id <= 0) {
     
    150153            await onAddComponentToBuild({buildId: id, componentId: component.id});
    151154        } catch (e) {
    152             // console.error("Failed to add component to server build", e);
    153155            alert("Failed to add component. Please try again.");
    154156        } finally {
     
    156158        }
    157159    };
     160
    158161
    159162    const handleRemovePart = async (slotId: string) => {
Note: See TracChangeset for help on using the changeset viewer.