|
Server IP : 217.21.85.138 / Your IP : 216.73.216.103 Web Server : LiteSpeed System : Linux in-mum-web906.main-hosting.eu 4.18.0-553.37.1.lve.el8.x86_64 #1 SMP Mon Feb 10 22:45:17 UTC 2025 x86_64 User : u915722082 ( 915722082) PHP Version : 7.4.33 Disable Function : system, exec, shell_exec, passthru, mysql_list_dbs, ini_alter, dl, symlink, link, chgrp, leak, popen, apache_child_terminate, virtual, mb_send_mail MySQL : OFF | cURL : ON | WGET : ON | Perl : OFF | Python : OFF Directory (0755) : /home/u915722082/.nvm/../public_html/lohri/user/ |
| [ Home ] | [ C0mmand ] | [ Upload File ] |
|---|
<?php
session_start();
include "../config/db.php";
// Check if user is logged in
if (!isset($_SESSION['user_id'])) {
header("Location: ../user/login.php");
exit;
}
$vendor_id = $_SESSION['user_data']['id'];
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Add Product - Vendor Dashboard</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<link href="../css/style.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300,400,500,600,700" />
<link href="../assets/plugins/custom/datatables/datatables.bundle.css" rel="stylesheet" type="text/css" />
<link href="../assets/plugins/global/plugins.bundle.css" rel="stylesheet" type="text/css" />
<link href="../assets/css/style.bundle.css" rel="stylesheet" type="text/css" />
<link href="add_product_css.css" rel="stylesheet" type="text/css" />
<style>
.defect-row {
border: 1px solid #e4e6ef;
border-radius: 8px;
padding: 20px;
margin-bottom: 15px;
background: #f9f9f9;
position: relative;
}
.defect-image-preview {
width: 100px;
height: 100px;
object-fit: cover;
border-radius: 6px;
border: 1px solid #ddd;
display: none;
}
.defect-image-preview.show {
display: block;
}
.remove-defect-btn {
position: absolute;
top: 10px;
right: 10px;
}
.add-defect-btn {
width: 100%;
border: 2px dashed #3b82f6;
background: #eff6ff;
color: #3b82f6;
padding: 15px;
border-radius: 8px;
font-weight: 500;
}
.add-defect-btn:hover {
background: #dbeafe;
border-color: #2563eb;
}
</style>
</head>
<body>
<?php include "../ui/nav.php"; ?>
<div class="page-content">
<div class="container">
<!-- Breadcrumb -->
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="index.php">Home</a></li>
<li class="breadcrumb-item"><a href="account.php">Account</a></li>
<li class="breadcrumb-item active" aria-current="page">Add product</li>
</ol>
</nav>
<div id="kt_app_content" class="app-content flex-column-fluid">
<div id="kt_app_content_container" class="app-container container-xxl">
<form id="kt_ecommerce_add_product_form" class="form d-flex flex-column flex-lg-row" enctype="multipart/form-data">
<input type="hidden" name="vendor_id" value="<?php echo htmlspecialchars($vendor_id); ?>">
<!--begin::Aside column-->
<div class="d-flex flex-column gap-7 gap-lg-10 w-100 w-lg-300px mb-7 me-lg-10">
<!--begin::Thumbnail settings-->
<div class="card card-flush py-4">
<div class="card-header">
<div class="card-title">
<h2>Thumbnail</h2>
</div>
</div>
<div class="card-body text-center pt-0">
<style>
.image-input-placeholder { background-image: url('../assets/media/svg/files/blank-image.svg'); }
[data-theme="dark"] .image-input-placeholder { background-image: url('../assets/media/svg/files/blank-image-dark.svg'); }
</style>
<div class="image-input image-input-empty image-input-outline image-input-placeholder mb-3" data-kt-image-input="true">
<div class="image-input-wrapper w-150px h-150px"></div>
<label class="btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow"
data-kt-image-input-action="change"
data-bs-toggle="tooltip"
title="Change avatar">
<i class="bi bi-pencil-fill fs-7"></i>
<input type="file" name="img" accept=".png, .jpg, .jpeg" id="imageUpload" required />
<input type="hidden" name="avatar_remove" />
</label>
<span class="btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow"
data-kt-image-input-action="cancel"
data-bs-toggle="tooltip"
title="Cancel avatar">
<i class="bi bi-x fs-2"></i>
</span>
<span class="btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow"
data-kt-image-input-action="remove"
data-bs-toggle="tooltip"
title="Remove avatar">
<i class="bi bi-x fs-2"></i>
</span>
</div>
<div class="text-muted fs-7">
Set the product thumbnail image. Only *.png, *.jpg and *.jpeg image files are accepted
</div>
</div>
</div>
<!--end::Thumbnail settings-->
<!--begin::Product Condition-->
<div class="card card-flush py-4">
<div class="card-header">
<div class="card-title">
<h2>Product Condition</h2>
</div>
</div>
<div class="card-body pt-0">
<div class="fv-row mb-7">
<div class="d-flex gap-4 mt-3">
<div class="form-check">
<input class="form-check-input" type="radio" name="product_condition"
id="brandNew" value="brand_new" checked>
<label class="form-check-label" for="brandNew">
Brand New
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="product_condition"
id="preLoved" value="pre_loved">
<label class="form-check-label" for="preLoved">
Pre-loved
</label>
</div>
</div>
<div class="text-muted fs-7 mt-4">Select the condition of the product.</div>
</div>
</div>
</div>
<!--end::Product Condition-->
</div>
<!--end::Aside column-->
<!--begin::Main column-->
<div class="d-flex flex-column flex-row-fluid gap-7 gap-lg-10">
<div class="tab-content">
<div class="tab-pane fade show active" id="kt_ecommerce_add_product_general" role="tab-panel">
<div class="d-flex flex-column gap-7 gap-lg-10">
<!--begin::General options-->
<div class="card card-flush py-4">
<div class="card-header">
<div class="card-title">
<h2>General</h2>
</div>
</div>
<div class="card-body pt-0">
<div class="mb-10 fv-row">
<label class="required form-label">Product Name</label>
<input type="text" class="form-control mb-2" placeholder="Product name" name="pname" required />
<div class="text-muted fs-7">A product name is required and recommended to be unique.</div>
</div>
<div>
<label class="form-label">Description</label>
<div id="kt_ecommerce_add_product_description" class="min-h-200px mb-2"></div>
<input type="hidden" name="pdes" id="pdes_hidden">
<div class="text-muted fs-7">Set a description to the product for better visibility.</div>
</div>
</div>
</div>
<!--end::General options-->
<!--begin::Media-->
<div class="card card-flush py-4">
<div class="card-header">
<div class="card-title">
<h2>Media</h2>
</div>
</div>
<div class="card-body pt-0">
<div class="fv-row mb-2">
<div class="file-upload-wrapper">
<label class="form-label required">Gallery</label>
<div class="file-upload-area" id="drop-area">
<div class="upload-content">
<svg class="upload-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="17 8 12 3 7 8"/>
<line x1="12" y1="3" x2="12" y2="15"/>
</svg>
<p class="upload-text">Drop files here or click to upload</p>
<p class="upload-hint">Upload up to 5 files</p>
<input type="file" name="img2[]" id="file-input" multiple accept=".png, .jpg, .jpeg" required>
</div>
</div>
<div class="preview-area" id="preview-area"></div>
</div>
</div>
<div class="text-muted fs-7">
<span class="text-primary">Please upload files under 1 MB </span>
and set the product media gallery to a maximum of 5 images
</div>
</div>
</div>
<!--end::Media-->
<!--begin::Pricing-->
<div class="card card-flush py-4">
<div class="card-header">
<div class="card-title">
<h2>Thrift Item Pricing</h2>
</div>
</div>
<div class="card-body pt-0">
<div class="row">
<div class="col-lg-6">
<div class="mb-10 fv-row">
<label class="required form-label">Actual Price</label>
<input type="number" name="actual_price" class="form-control mb-2" placeholder="Original purchase price" step="0.01" min="0" required />
<div class="text-muted fs-7">The original cost of the thrift item.</div>
</div>
</div>
<div class="col-lg-6">
<div class="mb-10 fv-row">
<label class="required form-label">Reselling Price</label>
<input type="number" name="reselling_price" class="form-control mb-2" placeholder="Customer selling price" step="0.01" min="0" required />
<div class="text-muted fs-7">The price customers will pay (excluding GST).</div>
</div>
</div>
<div class="col-lg-4" style="display:none">
<div class="mb-10 fv-row">
<label class="form-label">Customer Total Payment</label>
<input type="number" name="total_payment" class="form-control mb-2" placeholder="0" readonly style="background-color: #f3e5f5;" />
<div class="text-muted fs-7">Total amount customer pays (including GST).</div>
</div>
</div>
</div>
<!-- Hidden fields for calculated values -->
<input type="hidden" name="vendor_share">
<input type="hidden" name="admin_total">
</div>
</div>
<!--end::Pricing-->
<!--begin::Product Defects-->
<div class="card card-flush py-4">
<div class="card-header">
<div class="card-title">
<h2>Product Defects (Optional)</h2>
</div>
</div>
<div class="card-body pt-0">
<div id="defects-container" class="d-flex flex-column gap-5">
<!-- Defect rows will be added here dynamically -->
</div>
<div class="card-toolbar">
<button type="button" class="btn btn-sm btn-light-primary" onclick="addDefectRow()">
<i class="ki-duotone ki-plus fs-2"></i>
Add Defect
</button>
</div>
</div>
</div>
<!--end::Product Defects-->
</div>
</div>
</div>
<div class="d-flex justify-content-end mt-5">
<a href="../user/account.php" id="kt_ecommerce_add_product_cancel" class="btn btn-light me-5">Cancel</a>
<button type="submit" id="btn-submit" class="btn btn-primary">
<span class="indicator-label">Save Changes</span>
<span class="indicator-progress">
Please wait...
<span class="spinner-border spinner-border-sm align-middle ms-2"></span>
</span>
</button>
</div>
</div>
<!--end::Main column-->
</form>
</div>
</div>
</div>
</div>
<?php include "../ui/footer.php"; ?>
<script src="../assets/plugins/global/plugins.bundle.js"></script>
<script src="../assets/js/scripts.bundle.js"></script>
<script src="../assets/plugins/custom/datatables/datatables.bundle.js"></script>
<script src="../assets/plugins/custom/formrepeater/formrepeater.bundle.js"></script>
<script src="../assets/js/custom/apps/ecommerce/catalog/save-product.js"></script>
<script src="../assets/js/widgets.bundle.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
let defectCounter = 0;
// Add defect row with Metronic styling
function addDefectRow() {
defectCounter++;
const defectsContainer = document.getElementById('defects-container');
const emptyState = document.getElementById('empty-state');
// Hide empty state
if (emptyState) {
emptyState.style.display = 'none';
}
const defectRow = document.createElement('div');
defectRow.className = 'card card-bordered mb-xl-4';
defectRow.id = `defect-row-${defectCounter}`;
defectRow.innerHTML = `
<div class="card-header">
<h3 class="card-title">
<i class="ki-duotone ki-information-5 fs-2 text-warning me-2">
<span class="path1"></span>
<span class="path2"></span>
<span class="path3"></span>
</i>
Defect #${defectCounter}
</h3>
<div class="card-toolbar">
<button type="button" class="btn btn-sm btn-icon btn-light-danger" onclick="removeDefectRow(${defectCounter})">
<i class="ki-duotone ki-trash fs-2">
<span class="path1"></span>
<span class="path2"></span>
<span class="path3"></span>
<span class="path4"></span>
<span class="path5"></span>
</i>
</button>
</div>
</div>
<div class="card-body">
<div class="row g-5">
<div class="col-md-4">
<div class="fv-row">
<label class="form-label">Defect Image</label>
<div class="image-input image-input-outline" data-kt-image-input="true" style="background-image: url('../assets/media/svg/files/blank-image.svg')">
<div class="image-input-wrapper w-125px h-125px" id="defect-wrapper-${defectCounter}" style="background-image: url('../assets/media/svg/files/blank-image.svg')"></div>
<label class="btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow"
data-kt-image-input-action="change"
data-bs-toggle="tooltip"
title="Change image">
<i class="ki-duotone ki-pencil fs-7">
<span class="path1"></span>
<span class="path2"></span>
</i>
<input type="file" name="defect_images[]" class="defect-image-input"
accept=".png, .jpg, .jpeg" onchange="previewDefectImage(this, ${defectCounter})">
</label>
<span class="btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow"
data-kt-image-input-action="cancel"
data-bs-toggle="tooltip"
title="Cancel">
<i class="ki-duotone ki-cross fs-2">
<span class="path1"></span>
<span class="path2"></span>
</i>
</span>
<span class="btn btn-icon btn-circle btn-active-color-primary w-25px h-25px bg-body shadow"
data-kt-image-input-action="remove"
data-bs-toggle="tooltip"
title="Remove">
<i class="ki-duotone ki-cross fs-2">
<span class="path1"></span>
<span class="path2"></span>
</i>
</span>
</div>
<div class="form-text">Accepted formats: png, jpg, jpeg. Max 1MB</div>
</div>
</div>
<div class="col-md-8">
<div class="fv-row">
<label class="required form-label">Defect Description</label>
<input type="text" name="defect_titles[]" class="form-control form-control-solid"
placeholder="e.g., Small scratch on left side" required>
<div class="form-text">Clearly describe the defect to help buyers make informed decisions</div>
</div>
</div>
</div>
</div>
`;
defectsContainer.appendChild(defectRow);
// Initialize Metronic image input
KTImageInput.createInstances(`#defect-row-${defectCounter} [data-kt-image-input]`);
}
// Remove defect row
function removeDefectRow(id) {
const row = document.getElementById(`defect-row-${id}`);
const emptyState = document.getElementById('empty-state');
if (row) {
row.remove();
// Show empty state if no defects remain
const remainingDefects = document.querySelectorAll('#defects-container .card');
if (remainingDefects.length === 0 && emptyState) {
emptyState.style.display = 'flex';
}
}
}
// Preview defect image
function previewDefectImage(input, id) {
const file = input.files[0];
if (file) {
// Validate file type
if (!['image/png', 'image/jpeg', 'image/jpg'].includes(file.type)) {
Swal.fire({
icon: 'error',
title: 'Invalid File',
text: 'Only .png, .jpg, or .jpeg files are allowed!',
confirmButtonColor: '#3085d6'
});
input.value = '';
return;
}
// Validate file size (1MB)
if (file.size > 1048576) {
Swal.fire({
icon: 'error',
title: 'File Too Large',
text: 'Image size must be under 1 MB!',
confirmButtonColor: '#3085d6'
});
input.value = '';
return;
}
const reader = new FileReader();
reader.onload = function(e) {
const wrapper = document.getElementById(`defect-wrapper-${id}`);
if (wrapper) {
wrapper.style.backgroundImage = `url('${e.target.result}')`;
}
};
reader.readAsDataURL(file);
}
}
// Thumbnail validation
document.getElementById('imageUpload').addEventListener('change', function() {
const file = this.files[0];
if (file && !['image/png', 'image/jpeg'].includes(file.type)) {
Swal.fire({
icon: 'error',
title: 'Invalid File',
text: 'Only .png, .jpg, or .jpeg files are allowed!',
confirmButtonColor: '#3085d6'
});
this.value = '';
}
});
// Pricing calculation
document.addEventListener("DOMContentLoaded", function () {
const actualPrice = document.querySelector('input[name="actual_price"]');
const resellingPrice = document.querySelector('input[name="reselling_price"]');
const vendorShare = document.querySelector('input[name="vendor_share"]');
const totalPayment = document.querySelector('input[name="total_payment"]');
const adminTotal = document.querySelector('input[name="admin_total"]');
const GST_RATE = 5;
const ADMIN_COMMISSION_RATE = 25;
const VENDOR_SHARE_RATE = 75;
function calculatePricing() {
const actualPriceValue = parseFloat(actualPrice.value) || 0;
const basePriceValue = parseFloat(resellingPrice.value) || 0;
if (basePriceValue <= 0) {
vendorShare.value = '0.00';
totalPayment.value = '0.00';
adminTotal.value = '0.00';
return;
}
const calculatedVendorShare = (basePriceValue * VENDOR_SHARE_RATE) / 100;
const calculatedAdminCommission = (basePriceValue * ADMIN_COMMISSION_RATE) / 100;
const calculatedGSTAmount = (basePriceValue * GST_RATE) / 100;
const calculatedTotalPayment = basePriceValue + calculatedGSTAmount;
const calculatedAdminTotal = calculatedAdminCommission + calculatedGSTAmount;
vendorShare.value = calculatedVendorShare.toFixed(2);
totalPayment.value = calculatedTotalPayment.toFixed(2);
adminTotal.value = calculatedAdminTotal.toFixed(2);
}
function sanitizeNumericInput(input) {
input.value = input.value.replace(/[^0-9.]/g, '');
const parts = input.value.split('.');
if (parts.length > 2) {
input.value = parts[0] + '.' + parts.slice(1).join('');
}
if (parts[1] && parts[1].length > 2) {
input.value = parts[0] + '.' + parts[1].substring(0, 2);
}
}
actualPrice.addEventListener("input", function () {
sanitizeNumericInput(this);
calculatePricing();
});
resellingPrice.addEventListener("input", function () {
sanitizeNumericInput(this);
calculatePricing();
});
calculatePricing();
});
// Gallery media script
document.addEventListener("DOMContentLoaded", function () {
const dropArea = document.getElementById("drop-area");
const fileInput = document.getElementById("file-input");
const previewArea = document.getElementById("preview-area");
const MAX_FILES = 5;
const MAX_SIZE = 1048576; // 1MB
const ALLOWED_TYPES = ["image/jpeg", "image/png", "image/jpg"];
["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => {
dropArea.addEventListener(eventName, preventDefaults, false);
document.body.addEventListener(eventName, preventDefaults, false);
});
["dragenter", "dragover"].forEach((eventName) =>
dropArea.addEventListener(eventName, () => dropArea.classList.add("dragover"))
);
["dragleave", "drop"].forEach((eventName) =>
dropArea.addEventListener(eventName, () => dropArea.classList.remove("dragover"))
);
dropArea.addEventListener("drop", handleDrop);
dropArea.addEventListener("click", () => fileInput.click());
fileInput.addEventListener("change", handleFiles);
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
handleFiles({ target: { files: files } });
}
function handleFiles(e) {
const files = [...e.target.files];
const currentCount = previewArea.querySelectorAll(".preview-item").length;
if (currentCount + files.length > MAX_FILES) {
Swal.fire({
icon: "error",
title: "Too Many Files",
text: `You can upload a maximum of ${MAX_FILES} images.`,
confirmButtonText: "OK",
});
return;
}
files.forEach((file) => {
if (!ALLOWED_TYPES.includes(file.type)) {
Swal.fire({
icon: "error",
title: "Invalid File Format",
text: "Please select PNG, JPG, or JPEG files only.",
confirmButtonText: "OK",
});
return;
}
if (file.size > MAX_SIZE) {
Swal.fire({
icon: "error",
title: "File Too Large",
text: "Each image must be under 1MB.",
confirmButtonText: "OK",
});
return;
}
previewFile(file);
});
}
function formatFileSize(bytes) {
const k = 1024;
const sizes = ["Bytes", "KB", "MB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
}
function previewFile(file) {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = function () {
const div = document.createElement("div");
div.className = "preview-item";
div.style.position = "relative";
div.style.display = "inline-block";
div.style.margin = "10px";
div.innerHTML = `
<img src="${reader.result}" alt="${file.name}" style="width: 100px; height: 100px; object-fit: cover; border: 1px solid #ccc; border-radius: 6px;">
<div class="preview-overlay" style="text-align:center;">
<div class="file-size">${formatFileSize(file.size)}</div>
<button class="remove-btn btn btn-sm mt-2" onclick="this.closest('.preview-item').remove()">Remove</button>
</div>
`;
previewArea.appendChild(div);
};
}
});
// Form submission
$(document).ready(function () {
$("#kt_ecommerce_add_product_form").submit(function (event) {
event.preventDefault();
// Copy content from editor to hidden input
$("#pdes_hidden").val($("#kt_ecommerce_add_product_description").html());
// Validate form
if (!this.checkValidity()) {
this.reportValidity();
return;
}
const formData = new FormData(this);
formData.append("action", "Insert");
$.ajax({
url: "ajax_product.php",
type: "POST",
data: formData,
contentType: false,
processData: false,
beforeSend: function () {
$("#btn-submit").attr("disabled", true);
$("#btn-submit .indicator-label").addClass("d-none");
$("#btn-submit .indicator-progress").removeClass("d-none");
},
success: function (response) {
try {
let jsonResponse = typeof response === "string" ? JSON.parse(response.trim()) : response;
if (jsonResponse.success) {
Swal.fire({
title: "Success!",
text: jsonResponse.message,
icon: "success",
confirmButtonText: "Ok, got it!",
}).then(() => {
location.reload();
});
} else {
Swal.fire({
title: "Error",
text: jsonResponse.message || "An error occurred",
icon: "error",
confirmButtonText: "OK",
});
}
} catch (error) {
console.error("Response parsing error:", error);
Swal.fire({
title: "Error",
text: "Invalid response from server",
icon: "error",
confirmButtonText: "OK",
});
}
},
error: function (xhr, textStatus, errorThrown) {
console.error("AJAX Error:", { xhr, textStatus, errorThrown });
Swal.fire({
title: "Error",
text: "An error occurred while submitting the product.",
icon: "error",
confirmButtonText: "OK",
});
},
complete: function () {
$("#btn-submit").attr("disabled", false);
$("#btn-submit .indicator-label").removeClass("d-none");
$("#btn-submit .indicator-progress").addClass("d-none");
},
});
});
});
</script>
</body>
</html>