Changeset 958bc89 for pages/forge
- Timestamp:
- 12/29/25 04:34:44 (6 months ago)
- Branches:
- main
- Children:
- 41a2f81
- Parents:
- f46bf5c
- File:
-
- 1 edited
-
pages/forge/+Page.tsx (modified) (9 diffs)
Legend:
- Unmodified
- Added
- Removed
-
pages/forge/+Page.tsx
rf46bf5c r958bc89 3 3 Container, Paper, Typography, Box, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, 4 4 Button, IconButton, Avatar, TextField, Grid, Chip, CircularProgress, 5 Menu, MenuItem, ListItemIcon 5 Menu, MenuItem, ListItemIcon, Dialog, DialogTitle, DialogContent, DialogActions 6 6 } from '@mui/material'; 7 7 import AddIcon from '@mui/icons-material/Add'; … … 18 18 onRemoveComponentFromBuild, 19 19 onDeleteBuild, 20 onGetBuildState, onGetBuildComponents 20 onGetBuildState, 21 onGetBuildComponents 21 22 } from './forge.telefunc'; 22 23 … … 57 58 const [detailsOpen, setDetailsOpen] = useState<any>(null); 58 59 const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); 60 const [submitDialogOpen, setSubmitDialogOpen] = useState(false); // NEW 61 const [tempDescription, setTempDescription] = useState(""); // NEW 59 62 const isSubmittedRef = React.useRef(false); 60 63 … … 63 66 setTotalPrice(price); 64 67 }, [slots]); 68 69 useEffect(() => { 70 if (buildId && buildName.trim()) { 71 const timeoutId = setTimeout(() => { 72 saveBuildState({ buildId, name: buildName.trim(), description }); 73 }, 1000); 74 75 return () => clearTimeout(timeoutId); 76 } 77 }, [buildId, buildName, description]); 65 78 66 79 useEffect(() => { … … 91 104 if (urlBuildId && Number.isInteger(Number(urlBuildId)) && Number(urlBuildId) > 0) { 92 105 const loadBuildId = Number(urlBuildId); 93 onGetBuildComponents({ buildId: loadBuildId }) 94 .then(async (components) => { 95 if (components && components.length > 0) { 106 107 onGetBuildState({ buildId: loadBuildId }) 108 .then((buildState) => { 109 if (buildState) { 96 110 setBuildId(loadBuildId); 97 setBuildName("Cloned Build"); 98 setDescription(""); 99 100 const detailedComponents = await Promise.all( 101 components.map(async (c: any) => { 102 const full = await onGetComponentDetails({ componentId: c.id }).catch(() => null); 103 return full ? { ...c, ...full, details: full?.details } : c; 104 }) 105 ); 106 107 const componentMap = new Map(); 108 detailedComponents.forEach((c: any) => componentMap.set(c.type, c)); 109 110 setSlots(prevSlots => prevSlots.map(slot => { 111 const match = componentMap.get(slot.type); 112 return match ? { ...slot, component: match } : slot; 113 })); 114 115 window.history.replaceState({}, document.title, "/forge"); 111 setBuildName(buildState.build.name); 112 setDescription(buildState.build.description || ""); 116 113 } 117 114 }) 118 .catch(() => {}); 115 .catch(() => {}) 116 .finally(() => { 117 onGetBuildComponents({ buildId: loadBuildId }) 118 .then(async (components) => { 119 if (components && components.length > 0) { 120 const detailedComponents = await Promise.all( 121 components.map(async (c: any) => { 122 const full = await onGetComponentDetails({ componentId: c.id }).catch(() => null); 123 return full ? { ...c, ...full, details: full?.details } : c; 124 }) 125 ); 126 127 const componentMap = new Map(); 128 detailedComponents.forEach((c: any) => componentMap.set(c.type, c)); 129 130 setSlots(prevSlots => prevSlots.map(slot => { 131 const match = componentMap.get(slot.type); 132 return match ? { ...slot, component: match } : slot; 133 })); 134 135 window.history.replaceState({}, document.title, "/forge"); 136 } 137 }) 138 .catch(() => {}); 139 }); 119 140 } 120 141 }, []); … … 159 180 }; 160 181 161 162 182 const handleRemovePart = async (slotId: string) => { 163 183 const slot = slots.find(s => s.id === slotId); … … 199 219 }; 200 220 201 const handleSubmit = async() => {221 const handleSubmit = () => { 202 222 if (!buildName.trim()) return alert("Please name your build!"); 203 223 if (!buildId) return alert("You must add at least one component before submitting."); 204 224 225 setTempDescription(description || ""); 226 setSubmitDialogOpen(true); 227 }; 228 229 const handleSubmitConfirm = async () => { 230 if(!buildId) return; 231 205 232 setIsSubmitting(true); 233 setSubmitDialogOpen(false); 234 206 235 try { 236 await saveBuildState({ buildId, name: buildName.trim(), description: tempDescription }); 207 237 const result = await onEditBuild({buildId}); 208 209 238 if (!result) throw new Error("Failed to save build"); 210 239 … … 218 247 } 219 248 }; 220 221 249 222 250 const activeSlotType = useMemo(() => { … … 410 438 onClose={() => setDetailsOpen(null)} 411 439 /> 440 441 <Dialog open={submitDialogOpen} onClose={() => setSubmitDialogOpen(false)} maxWidth="sm" fullWidth> 442 <DialogTitle sx={{ bgcolor: '#ff8201', color: 'white', fontWeight: 'bold' }}> 443 Build Description 444 </DialogTitle> 445 <DialogContent sx={{ p: 3 }}> 446 <Typography variant="body1" sx={{ mb: 2, color: 'text.secondary' }}> 447 Add some notes about your build (optional): 448 </Typography> 449 <TextField 450 fullWidth 451 multiline 452 rows={4} 453 placeholder="e.g. Gaming build for 1440p, quiet operation, future-proof..." 454 value={tempDescription} 455 onChange={(e) => setTempDescription(e.target.value)} 456 variant="outlined" 457 /> 458 </DialogContent> 459 <DialogActions sx={{ p: 3, pt: 0 }}> 460 <Button onClick={() => setSubmitDialogOpen(false)}> 461 Cancel 462 </Button> 463 <Button 464 variant="contained" 465 color="primary" 466 onClick={handleSubmitConfirm} 467 disabled={isSubmitting} 468 > 469 {isSubmitting ? ( 470 <> 471 <CircularProgress size={20} sx={{ mr: 1 }} /> 472 Submitting... 473 </> 474 ) : ( 475 'Submit Build' 476 )} 477 </Button> 478 </DialogActions> 479 </Dialog> 412 480 </Container> 413 481 );
Note:
See TracChangeset
for help on using the changeset viewer.
