diff --git a/app/contact/api/engine/contact.php b/app/contact/api/engine/contact.php new file mode 100644 index 0000000..2853c86 --- /dev/null +++ b/app/contact/api/engine/contact.php @@ -0,0 +1,22 @@ +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)); + +?> \ No newline at end of file diff --git a/app/contact/api/engine/manage_contact.php b/app/contact/api/engine/manage_contact.php index 096b6fd..bff0e24 100644 --- a/app/contact/api/engine/manage_contact.php +++ b/app/contact/api/engine/manage_contact.php @@ -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)); diff --git a/app/contact/api/engine/retrieve_contact.php b/app/contact/api/engine/retrieve_contact.php new file mode 100644 index 0000000..f5ee242 --- /dev/null +++ b/app/contact/api/engine/retrieve_contact.php @@ -0,0 +1,29 @@ +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)); + +?> \ No newline at end of file diff --git a/app/contact/contact.php b/app/contact/contact.php index e58b25d..7db527c 100644 --- a/app/contact/contact.php +++ b/app/contact/contact.php @@ -89,7 +89,7 @@
- Total Products + Total Contact

356

@@ -135,7 +135,7 @@
- + @@ -186,13 +186,15 @@
- +
- - - - + + + + + + @@ -277,21 +279,22 @@ - function retrieve_product() { + function retrieve_contact() { return ajax_request({ - url: "inventory/api/engine/product.php", + 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 += ` - - - - + + + + + + `; }) - $("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); } diff --git a/app/contact/manage_contact.php b/app/contact/manage_contact.php index d8bb314..c12f9ad 100644 --- a/app/contact/manage_contact.php +++ b/app/contact/manage_contact.php @@ -32,41 +32,36 @@

Contact Information

- - + +
- - + +
- - + +
- - + +
- - + +
- -
@@ -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: "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: "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: "contact/api/engine/manage_contact.php", autoPrepare: true, @@ -191,14 +198,34 @@ } - function retreive_for_edit() { + function retrieve_contact_type() { + + return ajax_request({ + 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 += ``; + }) + $(`select#contact_type`).html(option); + } + }); + + } + + + function retrieve_for_edit() { return false; return ajax_request({ - url: "contact/api/engine/retrieve_contact_type.php", + 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, + "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); } diff --git a/app/include_topbar.php b/app/include_topbar.php index 7bd6986..fa58c0f 100644 --- a/app/include_topbar.php +++ b/app/include_topbar.php @@ -136,6 +136,7 @@ function log_out() { url: "login/api/engine/back.php", autoPrepare: true, checkRequired: 0, + action: 'read', onSuccess: function(res) { window.location.href = "index.php"; diff --git a/app/uploads/contact/699ebbfcb8756_1772010492.jpg b/app/uploads/contact/699ebbfcb8756_1772010492.jpg new file mode 100644 index 0000000..59f57f6 Binary files /dev/null and b/app/uploads/contact/699ebbfcb8756_1772010492.jpg differ diff --git a/app/uploads/contact/699ebd20c6071_1772010784.jpg b/app/uploads/contact/699ebd20c6071_1772010784.jpg new file mode 100644 index 0000000..59f57f6 Binary files /dev/null and b/app/uploads/contact/699ebd20c6071_1772010784.jpg differ diff --git a/app/uploads/contact/699ebd20c60a0_1772010784.jpg b/app/uploads/contact/699ebd20c60a0_1772010784.jpg new file mode 100644 index 0000000..cc77111 Binary files /dev/null and b/app/uploads/contact/699ebd20c60a0_1772010784.jpg differ diff --git a/app/uploads/contact/699ebe673aa9c_1772011111.jpg b/app/uploads/contact/699ebe673aa9c_1772011111.jpg new file mode 100644 index 0000000..59f57f6 Binary files /dev/null and b/app/uploads/contact/699ebe673aa9c_1772011111.jpg differ diff --git a/app/uploads/contact/699ebe673aad3_1772011111.jpg b/app/uploads/contact/699ebe673aad3_1772011111.jpg new file mode 100644 index 0000000..cc77111 Binary files /dev/null and b/app/uploads/contact/699ebe673aad3_1772011111.jpg differ diff --git a/app/uploads/contact/699ebe7825af3_1772011128.jpg b/app/uploads/contact/699ebe7825af3_1772011128.jpg new file mode 100644 index 0000000..59f57f6 Binary files /dev/null and b/app/uploads/contact/699ebe7825af3_1772011128.jpg differ diff --git a/app/uploads/contact/699ebe7825b2b_1772011128.jpg b/app/uploads/contact/699ebe7825b2b_1772011128.jpg new file mode 100644 index 0000000..cc77111 Binary files /dev/null and b/app/uploads/contact/699ebe7825b2b_1772011128.jpg differ diff --git a/app/uploads/contact/699ebe8c5852c_1772011148.png b/app/uploads/contact/699ebe8c5852c_1772011148.png new file mode 100644 index 0000000..7a8c665 Binary files /dev/null and b/app/uploads/contact/699ebe8c5852c_1772011148.png differ diff --git a/app/uploads/contact/PROD_699e9f164c1f9_1772003094.png b/app/uploads/contact/PROD_699e9f164c1f9_1772003094.png new file mode 100644 index 0000000..7a8c665 Binary files /dev/null and b/app/uploads/contact/PROD_699e9f164c1f9_1772003094.png differ diff --git a/app/uploads/contact/PROD_699e9fde79cc1_1772003294.jpg b/app/uploads/contact/PROD_699e9fde79cc1_1772003294.jpg new file mode 100644 index 0000000..59f57f6 Binary files /dev/null and b/app/uploads/contact/PROD_699e9fde79cc1_1772003294.jpg differ diff --git a/app/uploads/contact/PROD_699ea12587268_1772003621.jpg b/app/uploads/contact/PROD_699ea12587268_1772003621.jpg new file mode 100644 index 0000000..59f57f6 Binary files /dev/null and b/app/uploads/contact/PROD_699ea12587268_1772003621.jpg differ diff --git a/app/uploads/contact/PROD_699ea125872b6_1772003621.jpg b/app/uploads/contact/PROD_699ea125872b6_1772003621.jpg new file mode 100644 index 0000000..cc77111 Binary files /dev/null and b/app/uploads/contact/PROD_699ea125872b6_1772003621.jpg differ diff --git a/app/uploads/contact/PROD_699eacd26cf07_1772006610.jpg b/app/uploads/contact/PROD_699eacd26cf07_1772006610.jpg new file mode 100644 index 0000000..59f57f6 Binary files /dev/null and b/app/uploads/contact/PROD_699eacd26cf07_1772006610.jpg differ
ProductSKUCurrent StockMin StockTax ID/Citizen IDNameOrganizationBranchBilling AddressShipping Address Status Action
${item['product_name']}${item['sku']}${item['description']}${item['status']}${item['tax_id']}${item['contact_name']}${item['organization']}${item['branch']}${item['billing_address']}${item['shipping_address']} ${(item['status'])?'Active':'Inactive'} - +