DONE dropzone upload
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
require '../../../preset.php';
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
|
||||
$sql = "SELECT *
|
||||
FROM md_contact
|
||||
WHERE
|
||||
company_id = {$company_id}";
|
||||
$sth = $pdo2->prepare($sql);
|
||||
$sth->execute();
|
||||
if ($sth->errorInfo()[0] != "00000" && !empty($sth->errorInfo()[0])) {
|
||||
$answer["message"] = (empty($sth->errorInfo()[2])) ? $sth->errorInfo()[0] : $sth->errorInfo()[2];
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
$answer["output"] = $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
$answer["success"] = 1;
|
||||
exit(json_encode($answer));
|
||||
|
||||
?>
|
||||
@@ -6,9 +6,176 @@
|
||||
|
||||
$id = (int)$data['id'];
|
||||
|
||||
$answer["testA"] = $data;
|
||||
$answer["testB"] = $_FILES;
|
||||
exit(json_encode($answer));
|
||||
/** * FILE SYNCHRONIZATION & UPLOAD LOGIC
|
||||
* -----------------------------------------------------------------------------------
|
||||
* Strategy: "Keep & Sync"
|
||||
* 1. Identify which existing files were removed by the user and delete them from disk.
|
||||
* 2. Upload any brand-new files dropped into the Dropzone.
|
||||
* 3. Re-assemble a final comma-separated string containing (Kept Files + New Files).
|
||||
* -----------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
// Define the physical path for storage
|
||||
$target_dir = $include_url . "uploads/contact/";
|
||||
|
||||
/** 1. DISK CLEANUP (Removal of deleted files) **/
|
||||
if ($id > 0) {
|
||||
// Fetch currently stored filenames from DB for comparison
|
||||
$sql_check = "SELECT `files` FROM md_contact WHERE company_id = :company_id AND id = :id";
|
||||
$sth_check = $pdo2->prepare($sql_check);
|
||||
$sth_check->execute([":company_id" => $company_id, ":id" => $id]);
|
||||
$existing_db_string = $sth_check->fetchColumn();
|
||||
|
||||
if (!empty($existing_db_string)) {
|
||||
$existing_files_array = explode(',', $existing_db_string);
|
||||
|
||||
// 'keep_files' comes from JS (Dropzone.files names currently in the UI)
|
||||
$keep_files = isset($data['keep_files']) ? explode(',', $data['keep_files']) : [];
|
||||
|
||||
foreach ($existing_files_array as $file_on_disk) {
|
||||
$file_on_disk = trim($file_on_disk);
|
||||
if (empty($file_on_disk)) continue;
|
||||
|
||||
/**
|
||||
* If a file exists in the database but is NOT in the 'keep_files' list,
|
||||
* it means the user clicked 'Remove' in the UI. We delete it from the server.
|
||||
*/
|
||||
if (!in_array($file_on_disk, $keep_files)) {
|
||||
$full_path = $target_dir . $file_on_disk;
|
||||
if (file_exists($full_path)) {
|
||||
unlink($full_path); // Physically remove (rm) the file
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 2. PERMISSION & DIRECTORY CHECK **/
|
||||
$parent_dir = dirname($target_dir);
|
||||
if (!is_writable($parent_dir)) {
|
||||
exit(json_encode([
|
||||
"success" => 0,
|
||||
"message" => "Server error: Target directory is not writable. Check permissions."
|
||||
]));
|
||||
}
|
||||
|
||||
// Ensure the directory exists
|
||||
if (!is_dir($target_dir)) {
|
||||
mkdir($target_dir, 0755, true);
|
||||
}
|
||||
|
||||
/** 3. PROCESSING NEW UPLOADS **/
|
||||
$uploaded_names = [];
|
||||
|
||||
if (isset($_FILES['contact_files'])) {
|
||||
foreach ($_FILES['contact_files']['name'] as $key => $name) {
|
||||
if ($_FILES['contact_files']['error'][$key] === UPLOAD_ERR_OK) {
|
||||
|
||||
$tmp_name = $_FILES['contact_files']['tmp_name'][$key];
|
||||
$extension = pathinfo($name, PATHINFO_EXTENSION);
|
||||
|
||||
// Assign a unique ID to prevent filename collisions on the server
|
||||
$file_id = uniqid() . "_" . time() . "." . $extension;
|
||||
$destination = $target_dir . $file_id;
|
||||
|
||||
if (move_uploaded_file($tmp_name, $destination)) {
|
||||
$uploaded_names[] = $file_id;
|
||||
// Standard readable permission
|
||||
chmod($destination, 0644);
|
||||
} else {
|
||||
error_log("Failed to move uploaded file: " . $name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 4. CONSTRUCTING THE FINAL DATABASE STRING **/
|
||||
// Parse files the user kept from the existing session
|
||||
$keep_files = isset($data['keep_files']) ? explode(',', $data['keep_files']) : [];
|
||||
|
||||
// Merge old kept filenames with the brand-new unique filenames
|
||||
$final_file_array = array_merge($keep_files, $uploaded_names);
|
||||
|
||||
// Clean up: Remove empty values and trim whitespace
|
||||
$final_file_array = array_filter(array_map('trim', $final_file_array));
|
||||
|
||||
// The final comma-separated string for the DB 'files' column
|
||||
$db_image_string = implode(",", $final_file_array);
|
||||
|
||||
/********************************************************************************** */
|
||||
|
||||
|
||||
// 2. Initialize log array
|
||||
$table_log = [];
|
||||
|
||||
// Get existing log and existing files if updating
|
||||
if ($id > 0) {
|
||||
$sql = "SELECT `log`, `files` FROM md_contact WHERE company_id = :company_id AND id = :id";
|
||||
$sth = $pdo2->prepare($sql);
|
||||
$sth->execute([
|
||||
":company_id" => $company_id,
|
||||
":id" => $id
|
||||
]);
|
||||
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
if ($row) {
|
||||
$table_log = json_decode($row['log'] ?? '[]', true);
|
||||
}
|
||||
}
|
||||
|
||||
// Append the new log entry
|
||||
$table_log[] = $logging;
|
||||
|
||||
// 3. Database Operation
|
||||
$params = [
|
||||
":company_id" => $company_id,
|
||||
":contact_name" => $data["contact_name"],
|
||||
":tax_id" => $data["tax_id"],
|
||||
":organization" => $data["organization"],
|
||||
":branch" => $data["branch"],
|
||||
":contact_type" => (int)$data["contact_type"],
|
||||
":billing_address" => $data["billing_address"],
|
||||
":shipping_location"=> $data["shipping_location"],
|
||||
":shipping_address" => $data["shipping_address"],
|
||||
":remark" => $data["remark"],
|
||||
":files" => $db_image_string,
|
||||
":status" => (int)$data["status"],
|
||||
":log" => json_encode($table_log)
|
||||
];
|
||||
|
||||
if ($id > 0) {
|
||||
// UPDATE
|
||||
$sql = "UPDATE md_contact SET
|
||||
`contact_name` = :contact_name,
|
||||
`tax_id` = :tax_id,
|
||||
`organization` = :organization,
|
||||
`branch` = :branch,
|
||||
`contact_type` = :contact_type,
|
||||
`billing_address` = :billing_address,
|
||||
`shipping_location` = :shipping_location,
|
||||
`shipping_address` = :shipping_address,
|
||||
`remark` = :remark,
|
||||
`files` = :files,
|
||||
`status` = :status,
|
||||
`log` = :log
|
||||
WHERE id = :id AND company_id = :company_id";
|
||||
|
||||
$params[":id"] = $id;
|
||||
$sth = $pdo2->prepare($sql);
|
||||
$sth->execute($params);
|
||||
|
||||
} else {
|
||||
// INSERT
|
||||
$sql = "INSERT INTO md_contact
|
||||
(company_id, contact_name, tax_id, organization, branch, contact_type,
|
||||
billing_address, shipping_location, shipping_address, remark, files, status, log)
|
||||
VALUES
|
||||
(:company_id, :contact_name, :tax_id, :organization, :branch, :contact_type,
|
||||
:billing_address, :shipping_location, :shipping_address, :remark, :files, :status, :log)";
|
||||
|
||||
$sth = $pdo2->prepare($sql);
|
||||
$sth->execute($params);
|
||||
}
|
||||
|
||||
$answer["success"] = 1;
|
||||
exit(json_encode($answer));
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
require '../../../preset.php';
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
|
||||
$id = (int)$data['id'];
|
||||
|
||||
$sql = "SELECT *
|
||||
FROM md_contact
|
||||
WHERE
|
||||
company_id = {$company_id} and
|
||||
id = {$id}";
|
||||
|
||||
$sth = $pdo2->prepare($sql);
|
||||
$sth->execute();
|
||||
if ($sth->errorInfo()[0] != "00000" && !empty($sth->errorInfo()[0])) {
|
||||
$answer["message"] = (empty($sth->errorInfo()[2])) ? $sth->errorInfo()[0] : $sth->errorInfo()[2];
|
||||
exit(json_encode($answer));
|
||||
}
|
||||
|
||||
$target_dir = $include_url."uploads/contact/";
|
||||
|
||||
$answer["contact_dir"] = $target_dir;
|
||||
$answer["output"] = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
$answer["success"] = 1;
|
||||
exit(json_encode($answer));
|
||||
|
||||
?>
|
||||
+27
-22
@@ -89,7 +89,7 @@
|
||||
<i class="ti ti-box-seam fs-4 "></i>
|
||||
</div>
|
||||
<div class="">
|
||||
<span>Total Products</span>
|
||||
<span>Total Contact</span>
|
||||
<h2 class="fw-bold mb-0">356</h2>
|
||||
</div>
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
|
||||
<div class="col-lg-12 mb-5">
|
||||
<div class="position-relative">
|
||||
<input type="search" class="form-control ps-9" placeholder="Search by product name or SKU">
|
||||
<input type="search" class="form-control ps-9" placeholder="Search by contact name">
|
||||
<span class="position-absolute top-25 ms-4">
|
||||
<i class="ti ti-search text-muted"></i>
|
||||
|
||||
@@ -186,13 +186,15 @@
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 table-centered" id="product">
|
||||
<table class="table table-hover mb-0 table-centered" id="contact">
|
||||
<thead class="table-secondary border-light">
|
||||
<tr>
|
||||
<th>Product</th>
|
||||
<th>SKU</th>
|
||||
<th>Current Stock</th>
|
||||
<th>Min Stock</th>
|
||||
<th>Tax ID/Citizen ID</th>
|
||||
<th>Name</th>
|
||||
<th>Organization</th>
|
||||
<th>Branch</th>
|
||||
<th>Billing Address</th>
|
||||
<th>Shipping Address</th>
|
||||
<th>Status</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
@@ -277,21 +279,22 @@
|
||||
|
||||
|
||||
|
||||
function retrieve_product() {
|
||||
function retrieve_contact() {
|
||||
|
||||
return ajax_request({
|
||||
url: "<?php echo $server_url?>inventory/api/engine/product.php",
|
||||
url: "<?php echo $server_url?>contact/api/engine/contact.php",
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'read',
|
||||
onSuccess: function(res) {
|
||||
|
||||
// Register the array as a table name
|
||||
alasql('CREATE TABLE IF NOT EXISTS product');
|
||||
alasql.tables.product.data = res.output || [];
|
||||
alasql('CREATE TABLE IF NOT EXISTS contact');
|
||||
alasql.tables.contact.data = res.output || [];
|
||||
|
||||
var total_records = alasql.tables.product.data.length;
|
||||
var total_records = alasql.tables.contact.data.length;
|
||||
|
||||
var table_id = 'product';
|
||||
var table_id = 'contact';
|
||||
|
||||
var page = generate_pagination(table_id,total_records);
|
||||
$(`table#${table_id} tfoot`).html(page);
|
||||
@@ -308,37 +311,39 @@
|
||||
}
|
||||
|
||||
|
||||
function change_page_product(page_num) {
|
||||
function change_page_contact(page_num) {
|
||||
|
||||
var offset = (page_num - 1) * prop_limit;
|
||||
|
||||
var page_data = alasql(`SELECT * FROM product LIMIT ${prop_limit} OFFSET ${offset}`);
|
||||
var page_data = alasql(`SELECT * FROM contact LIMIT ${prop_limit} OFFSET ${offset}`);
|
||||
|
||||
var body = ``;
|
||||
$.each(page_data, function(key, item) {
|
||||
body += `<tr>
|
||||
<td class="py-3">${item['product_name']}</td>
|
||||
<td class="py-3">${item['sku']}</td>
|
||||
<td class="py-3">${item['description']}</td>
|
||||
<td class="py-3">${item['status']}</td>
|
||||
<td class="py-3">${item['tax_id']}</td>
|
||||
<td class="py-3">${item['contact_name']}</td>
|
||||
<td class="py-3">${item['organization']}</td>
|
||||
<td class="py-3">${item['branch']}</td>
|
||||
<td class="py-3">${item['billing_address']}</td>
|
||||
<td class="py-3">${item['shipping_address']}</td>
|
||||
<td class="py-3">
|
||||
${(item['status'])?'<span class="badge bg-success">Active</span>':'<span class="badge bg-secondary">Inactive</span>'}
|
||||
</td>
|
||||
<td class="py-3">
|
||||
<a href="<?php echo $server_url?>inventory/manage_product.php?id=${item['id']}" class=""><i class="ti ti-eye fs-5"></i></a>
|
||||
<a href="<?php echo $server_url?>contact/manage_contact.php?id=${item['id']}" class=""><i class="ti ti-eye fs-5"></i></a>
|
||||
<a href="#" class="link-danger"><i class="ti ti-trash ms-2 fs-5"></i></a>
|
||||
</td>
|
||||
</tr>`;
|
||||
})
|
||||
|
||||
$("table#product > tbody").html(body);
|
||||
$("table#contact > tbody").html(body);
|
||||
|
||||
}
|
||||
|
||||
$(async function() {
|
||||
try {
|
||||
await retrieve_contact_type();
|
||||
// await retrieve_product();
|
||||
await retrieve_contact();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
@@ -32,41 +32,36 @@
|
||||
<h2 class="fs-4 mb-3">Contact Information</h2>
|
||||
<div class="row g-5">
|
||||
<div class="col-lg-6">
|
||||
<label for="productName" class="form-label">Contact Name</label>
|
||||
<input type="text" name="" id="productName" class="form-control" placeholder="Name">
|
||||
<label for="contact_name" class="form-label">Contact Name</label>
|
||||
<input type="text" name="" id="contact_name" class="form-control" placeholder="Name" required>
|
||||
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<label for="productName" class="form-label">Tax ID/Citizen ID</label>
|
||||
<input type="text" name="" id="productName" class="form-control" placeholder="xxxxxxxxxxxxx">
|
||||
<label for="tax_id" class="form-label">Tax ID/Citizen ID</label>
|
||||
<input type="text" name="" id="tax_id" class="form-control" placeholder="xxxxxxxxxxxxx" required>
|
||||
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<label for="sku" class="form-label"> Organization</label>
|
||||
<input type="text" name="" id="sku" class="form-control" placeholder="company name">
|
||||
<label for="organization" class="form-label"> Organization</label>
|
||||
<input type="text" name="" id="organization" class="form-control" placeholder="company name" required>
|
||||
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<label for="sku" class="form-label"> Branch</label>
|
||||
<input type="text" name="" id="sku" class="form-control" placeholder="00000">
|
||||
<label for="branch" class="form-label"> Branch</label>
|
||||
<input type="text" name="" id="branch" class="form-control" placeholder="00000">
|
||||
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<label for="catogires" class="form-label"> Contact Type</label>
|
||||
<select name="" id="catogires" class="form-select">
|
||||
<option value="Leptop">Leptop</option>
|
||||
<option value="Desktop">Desktop</option>
|
||||
<option value="Accessories">Accessories</option>
|
||||
<option value="Phone">Phone</option>
|
||||
</select>
|
||||
<label for="contact_type" class="form-label"> Contact Type</label>
|
||||
<select name="" id="contact_type" class="form-select" required></select>
|
||||
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
|
||||
</div>
|
||||
<div class="col-lg-12">
|
||||
<label for="address" class="form-label"> Billing Address</label>
|
||||
<textarea name="address" rows="3" id="address" class="form-control"
|
||||
<label for="billing_address" class="form-label"> Billing Address</label>
|
||||
<textarea name="billing_address" rows="3" id="billing_address" class="form-control"
|
||||
placeholder="Customer Billing Address."></textarea>
|
||||
|
||||
</div>
|
||||
@@ -155,15 +150,18 @@
|
||||
let myDropzone;
|
||||
|
||||
$(document).ready(function() {
|
||||
// Use the ID to initialize, ensuring NO conflict with theme classes
|
||||
myDropzone = new Dropzone("#custom-dropzone", {
|
||||
url: "<?php echo $server_url?>contact/api/engine/manage_contact.php",
|
||||
autoProcessQueue: false,
|
||||
uploadMultiple: true,
|
||||
paramName: "contact_files"
|
||||
});
|
||||
// Use the ID to initialize, ensuring NO conflict with theme classes
|
||||
myDropzone = new Dropzone("#custom-dropzone", {
|
||||
url: "<?php echo $server_url?>contact/api/engine/manage_contact.php",
|
||||
autoProcessQueue: false, // Important: Wait for the button click
|
||||
uploadMultiple: true,
|
||||
parallelUploads: 10,
|
||||
acceptedFiles: "image/*",
|
||||
addRemoveLinks: true,
|
||||
paramName: "contact_files"
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function manage_contact() {
|
||||
let formData = new FormData();
|
||||
|
||||
@@ -174,6 +172,15 @@
|
||||
formData.append('contact_files[]', file);
|
||||
})
|
||||
|
||||
let keepFiles = [];
|
||||
myDropzone.files.forEach(function(file) {
|
||||
// If the file doesn't have a status or is already 'success', it's an existing file
|
||||
if (file.status === undefined || file.status === Dropzone.SUCCESS || file.status === Dropzone.ADDED) {
|
||||
keepFiles.push(file.name);
|
||||
}
|
||||
});
|
||||
formData.append('keep_files', keepFiles.join(','));
|
||||
|
||||
return ajax_request({
|
||||
url: "<?php echo $server_url?>contact/api/engine/manage_contact.php",
|
||||
autoPrepare: true,
|
||||
@@ -191,14 +198,34 @@
|
||||
}
|
||||
|
||||
|
||||
function retreive_for_edit() {
|
||||
function retrieve_contact_type() {
|
||||
|
||||
return ajax_request({
|
||||
url: "<?php echo $server_url?>contact/api/engine/contact_type.php",
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'read',
|
||||
onSuccess: function(res) {
|
||||
|
||||
var option = ``;
|
||||
$.each(res.output, function(key, item) {
|
||||
option += `<option value='${item.id}'>${item.contact_type}</option>`;
|
||||
})
|
||||
$(`select#contact_type`).html(option);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
function retrieve_for_edit() {
|
||||
|
||||
<?php if(empty($_GET['id'])){ ?>
|
||||
return false;
|
||||
<?php } ?>
|
||||
|
||||
return ajax_request({
|
||||
url: "<?php echo $server_url?>contact/api/engine/retrieve_contact_type.php",
|
||||
url: "<?php echo $server_url?>contact/api/engine/retrieve_contact.php",
|
||||
autoPrepare: true,
|
||||
checkRequired: 0,
|
||||
action: 'read',
|
||||
@@ -208,6 +235,32 @@
|
||||
$(`#${key}`).val(item);
|
||||
});
|
||||
|
||||
let item = res.output;
|
||||
// 2. Handle files specifically (No loop needed for the row)
|
||||
if (item.files) {
|
||||
let fileArray = item.files.split(',');
|
||||
|
||||
fileArray.forEach(function(fileName) {
|
||||
if (fileName.trim() !== "") {
|
||||
// Create a mock file for Dropzone
|
||||
let mockFile = {
|
||||
name: fileName,
|
||||
size: 12345,
|
||||
accepted: true // Prevents Dropzone from rejecting it if it's already on server
|
||||
};
|
||||
|
||||
// Display it in the UI
|
||||
myDropzone.displayExistingFile(
|
||||
mockFile,
|
||||
"<?php echo $server_url?>uploads/contact/" + fileName
|
||||
);
|
||||
|
||||
// Add to Dropzone's internal file array so it knows it exists
|
||||
myDropzone.files.push(mockFile);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(`button[type=submit]`).text('Update');
|
||||
|
||||
$(`button[type=reset]`).hide();
|
||||
@@ -221,7 +274,8 @@
|
||||
|
||||
$(async function() {
|
||||
try {
|
||||
await retreive_for_edit();
|
||||
await retrieve_contact_type();
|
||||
await retrieve_for_edit();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user