Skip to content

Commit d7df384

Browse files
authored
Merge pull request #329 from AMTuttle02/prod
Release v4.1.0
2 parents 23b9bc4 + 1137653 commit d7df384

File tree

2 files changed

+175
-10
lines changed

2 files changed

+175
-10
lines changed

API/src/product/updateProductDetails.php

Lines changed: 109 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,123 @@
2828
}
2929
$styleLocation = $_POST["default_style_location"];
3030
$customDetailsRequired = $_POST["customFieldRequired"];
31-
$product_id = $_SESSION["product_id"];
31+
// prefer explicit product_id from POST, fallback to session
32+
$product_id = isset($_POST['product_id']) ? intval($_POST['product_id']) : (isset($_SESSION["product_id"]) ? intval($_SESSION["product_id"]) : 0);
33+
if ($product_id === 0) {
34+
echo json_encode(array('success' => false, 'error' => 'product_id not provided'));
35+
exit;
36+
}
3237
$sizeAvailable = $_POST["sizeAvailable"];
3338

34-
// Attempt to insert new design into table
39+
// fetch existing filenames
40+
$fstmt = $conn->prepare("SELECT filename_front, filename_back FROM products WHERE product_id = ? LIMIT 1");
41+
$fstmt->bind_param("i", $product_id);
42+
$fstmt->execute();
43+
$fres = $fstmt->get_result();
44+
$existingFront = '';
45+
$existingBack = '';
46+
if ($fres && $fres->num_rows > 0) {
47+
$frow = $fres->fetch_assoc();
48+
$existingFront = $frow['filename_front'];
49+
$existingBack = $frow['filename_back'];
50+
}
51+
52+
$newFront = $existingFront;
53+
$newBack = $existingBack;
54+
55+
// handle explicit removal flags (delete current file and clear DB filename)
56+
if (isset($_POST['remove_front']) && $_POST['remove_front'] == '1') {
57+
// Prevent deleting the side that is configured as the default style location
58+
if ($styleLocation === 'front') {
59+
echo json_encode(array('success' => false, 'error' => 'Cannot delete front design because default style location is front'));
60+
exit;
61+
}
62+
if ($existingFront && $existingFront !== '') {
63+
$oldPath = UPLOAD_DIR . $existingFront;
64+
if (file_exists($oldPath)) {@unlink($oldPath);}
65+
}
66+
$newFront = '';
67+
}
68+
69+
if (isset($_POST['remove_back']) && $_POST['remove_back'] == '1') {
70+
// Prevent deleting the side that is configured as the default style location
71+
if ($styleLocation === 'back') {
72+
echo json_encode(array('success' => false, 'error' => 'Cannot delete back design because default style location is back'));
73+
exit;
74+
}
75+
if ($existingBack && $existingBack !== '') {
76+
$oldPath = UPLOAD_DIR . $existingBack;
77+
if (file_exists($oldPath)) {@unlink($oldPath);}
78+
}
79+
$newBack = '';
80+
}
81+
82+
// handle uploaded files if provided
83+
if (isset($_FILES['frontFile']) && $_FILES['frontFile']['error'] === UPLOAD_ERR_OK) {
84+
$file = $_FILES['frontFile'];
85+
$orig = basename($file['name']);
86+
// check DB for existing use of this filename by other products
87+
$check = $conn->prepare("SELECT product_id FROM products WHERE (filename_front = ? OR filename_back = ?) AND product_id != ? LIMIT 1");
88+
$check->bind_param("ssi", $orig, $orig, $product_id);
89+
$check->execute();
90+
$cres = $check->get_result();
91+
if ($cres && $cres->num_rows > 0) {
92+
echo json_encode(array('success' => false, 'error' => 'Filename "' . $orig . '" is already used by another product'));
93+
exit;
94+
}
95+
$target = UPLOAD_DIR . $orig;
96+
// delete the old product file first (per requirement)
97+
if ($existingFront && $existingFront !== '') {
98+
$oldPath = UPLOAD_DIR . $existingFront;
99+
if (file_exists($oldPath)) {@unlink($oldPath);}
100+
}
101+
// if a file already exists at the target filename, remove it so we cleanly replace
102+
if (file_exists($target)) {@unlink($target);}
103+
if (!move_uploaded_file($file['tmp_name'], $target)) {
104+
echo json_encode(array('success' => false, 'error' => 'Cannot upload front file'));
105+
exit;
106+
}
107+
$newFront = $orig;
108+
}
109+
110+
if (isset($_FILES['backFile']) && $_FILES['backFile']['error'] === UPLOAD_ERR_OK) {
111+
$file = $_FILES['backFile'];
112+
$orig = basename($file['name']);
113+
// check DB for existing use of this filename by other products
114+
$check = $conn->prepare("SELECT product_id FROM products WHERE (filename_front = ? OR filename_back = ?) AND product_id != ? LIMIT 1");
115+
$check->bind_param("ssi", $orig, $orig, $product_id);
116+
$check->execute();
117+
$cres = $check->get_result();
118+
if ($cres && $cres->num_rows > 0) {
119+
echo json_encode(array('success' => false, 'error' => 'Filename "' . $orig . '" is already used by another product'));
120+
exit;
121+
}
122+
$target = UPLOAD_DIR . $orig;
123+
// delete the old product file first (per requirement)
124+
if ($existingBack && $existingBack !== '') {
125+
$oldPath = UPLOAD_DIR . $existingBack;
126+
if (file_exists($oldPath)) {@unlink($oldPath);}
127+
}
128+
// if a file already exists at the target filename, remove it so we cleanly replace
129+
if (file_exists($target)) {@unlink($target);}
130+
if (!move_uploaded_file($file['tmp_name'], $target)) {
131+
echo json_encode(array('success' => false, 'error' => 'Cannot upload back file'));
132+
exit;
133+
}
134+
$newBack = $orig;
135+
}
136+
137+
// Update product including possible new filenames
35138
$query = $conn->prepare("UPDATE products
36-
SET product_name = ?, price = ?, tag_list = ?, tColors = ?, lColors = ?, cColors = ?, hColors = ?, categories = ?, subcategories = ?, default_style = ?, style_size = ?, default_style_location = ?, CustomDetailsRequired = ?, sizesAvailable = ?
139+
SET product_name = ?, price = ?, tag_list = ?, tColors = ?, lColors = ?, cColors = ?, hColors = ?, categories = ?, subcategories = ?, default_style = ?, style_size=?, default_style_location = ?, CustomDetailsRequired = ?, sizesAvailable = ?, filename_front = ?, filename_back = ?
37140
WHERE product_id = ?;");
38-
$query->bind_param("sssssssssssssss", $productName, $price, $tags, $tColors, $lColors, $cColors, $hColors, $categories, $subcategories, $defaultStyle, $styleSize, $styleLocation, $customDetailsRequired, $sizeAvailable, $product_id);
141+
$query->bind_param("ssssssssssssssssi", $productName, $price, $tags, $tColors, $lColors, $cColors, $hColors, $categories, $subcategories, $defaultStyle, $styleSize, $styleLocation, $customDetailsRequired, $sizeAvailable, $newFront, $newBack, $product_id);
39142
if (!$query->execute()) {
40143
// If insertion fails, return error message
41-
echo json_encode("ERR: Insertion failed to execute" . $query->error);
144+
echo json_encode(array('success' => false, 'error' => 'DB update failed: ' . $query->error));
42145
}
43146
else {
44-
echo json_encode(1);
147+
echo json_encode(array('success' => true));
45148
}
46149
}
47150

react/src/products/EditProducts.jsx

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ function EditProducts() {
1616
const [lColors, setLColors] = useState("");
1717
const [cColors, setCColors] = useState("");
1818
const [hColors, setHColors] = useState("");
19+
const [frontFileName, setFrontFileName] = useState("");
20+
const [backFileName, setBackFileName] = useState("");
21+
const [frontFile, setFrontFile] = useState(null);
22+
const [backFile, setBackFile] = useState(null);
1923
const [allSubcategories, setAllSubcategories] = useState([]);
2024
const [currentSubcategories, setCurrentSubcategories] = useState("");
2125
const [allCategories, setAllCategories] = useState([]);
@@ -28,6 +32,9 @@ function EditProducts() {
2832
const [customFieldRequired, setCustomFieldRequired] = useState(0);
2933
const [sizesAvailable, setSizesAvailable] = useState(1);
3034
const [failToUpdate, setFailToUpdate] = useState(false);
35+
const [uploadError, setUploadError] = useState("");
36+
const [removeFront, setRemoveFront] = useState(false);
37+
const [removeBack, setRemoveBack] = useState(false);
3138

3239
// API Calls
3340
useEffect(() => {
@@ -77,6 +84,8 @@ function EditProducts() {
7784
setHColors(data.hColors);
7885
setStyle(data.default_style);
7986
setLocation(data.default_style_location);
87+
setFrontFileName(data.filename_front || "");
88+
setBackFileName(data.filename_back || "");
8089
setStyleSize(data.style_size || "");
8190
setProductIsSet(true);
8291
setCustomFieldRequired(data.CustomDetailsRequired.toString());
@@ -165,14 +174,19 @@ function EditProducts() {
165174
} else if (style === 'hoodie' && hColors.trim() === '') {
166175
setFailToUpdate(true);
167176
} else {
168-
const formData = new FormData();
177+
const formData = new FormData();
169178
formData.append('productName', productName);
179+
formData.append('product_id', productId);
170180
formData.append('price', price);
171181
formData.append('tags', tagList);
172182
formData.append('tColors', tColors);
173183
formData.append('lColors', lColors);
174184
formData.append('cColors', cColors);
175185
formData.append('hColors', hColors);
186+
if (frontFile) formData.append('frontFile', frontFile);
187+
if (backFile) formData.append('backFile', backFile);
188+
if (removeFront) formData.append('remove_front', '1');
189+
if (removeBack) formData.append('remove_back', '1');
176190
// send semicolon-separated id lists for categories and subcategories
177191
formData.append('categories', selectedCategoryIds.join(';'));
178192
formData.append('subcategories', selectedSubcategoryIds.join(';'));
@@ -188,13 +202,20 @@ function EditProducts() {
188202
})
189203
.then((response) => response.json())
190204
.then((data) => {
191-
if(data) {
192-
window.location.href="/products";
205+
if (data && data.success) {
206+
window.location.href = "/products";
207+
} else {
208+
setUploadError((data && data.error) ? data.error : 'Update failed');
193209
}
194-
});
210+
})
211+
.catch((err) => setUploadError('Update failed'));
195212
}
196213
};
197214

215+
const uploadImage = async (side) => {
216+
// removed: uploadImage now handled on form submit
217+
}
218+
198219
if (admin) {
199220
return (
200221
<div className="EditProducts">
@@ -464,6 +485,36 @@ function EditProducts() {
464485
</div>
465486
<br/>
466487
<br/>
488+
<h3><b>Front Design</b> <small>Current: {frontFileName ? frontFileName : 'None'}</small></h3>
489+
<div className="containerRow">
490+
<input type="file" accept="image/*" disabled={removeFront} onChange={(e) => setFrontFile(e.target.files[0])} />
491+
{!removeFront ? (
492+
location === 'front' ? (
493+
<button type="button" className="delete-button" disabled title="Cannot delete the default-style image">Delete Front</button>
494+
) : (
495+
<button type="button" className="delete-button" onClick={() => setRemoveFront(true)}>Delete Front</button>
496+
)
497+
) : (
498+
<button type="button" className="default-button" onClick={() => setRemoveFront(false)}>Undo Delete</button>
499+
)}
500+
</div>
501+
<br/>
502+
<br/>
503+
<h3><b>Back Design</b> <small>Current: {backFileName ? backFileName : 'None'}</small></h3>
504+
<div className="containerRow">
505+
<input type="file" accept="image/*" disabled={removeBack} onChange={(e) => setBackFile(e.target.files[0])} />
506+
{!removeBack ? (
507+
location === 'back' ? (
508+
<button type="button" className="delete-button" disabled title="Cannot delete the default-style image">Delete Back</button>
509+
) : (
510+
<button type="button" className="delete-button" onClick={() => setRemoveBack(true)}>Delete Back</button>
511+
)
512+
) : (
513+
<button type="button" className="default-button" onClick={() => setRemoveBack(false)}>Undo Delete</button>
514+
)}
515+
</div>
516+
<br/>
517+
<br/>
467518
<button type="submit" className="default-button">Update Product</button>
468519
</form>
469520
{failToUpdate &&
@@ -477,6 +528,17 @@ function EditProducts() {
477528
</div>
478529
</div>
479530
}
531+
{uploadError &&
532+
<div className="confirmation-modal">
533+
<div className="confirmation-dialog">
534+
<h3>Upload Error</h3>
535+
<p>{uploadError}</p>
536+
<div className="confirmation-buttons">
537+
<button className="delete-button" onClick={() => setUploadError("")}>Close</button>
538+
</div>
539+
</div>
540+
</div>
541+
}
480542
</div>
481543
</div>
482544
</div>

0 commit comments

Comments
 (0)