import xls update

This commit is contained in:
Claudio 2025-03-08 08:34:00 +01:00
parent e9bad2260f
commit 8cad59e3d6
19 changed files with 2583 additions and 209 deletions

View File

@ -41,6 +41,7 @@
"laravel/tinker": "^2.7",
"laravel/ui": "^4.0",
"phpmailer/phpmailer": "^6.9",
"phpoffice/phpspreadsheet": "^4.1",
"proengsoft/laravel-jsvalidation": "^4.0.0",
"spatie/laravel-query-builder": "^5.0",
"vanguardapp/activity-log": "^6.0",

450
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "3d85faea6470d55b627da7cc0e8c9a67",
"content-hash": "a7749f3b43e37d8fb607bcccd227f5ee",
"packages": [
{
"name": "akaunting/laravel-setting",
@ -320,6 +320,85 @@
],
"time": "2024-02-09T16:56:22+00:00"
},
{
"name": "composer/pcre",
"version": "3.2.0",
"source": {
"type": "git",
"url": "https://github.com/composer/pcre.git",
"reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/pcre/zipball/ea4ab6f9580a4fd221e0418f2c357cdd39102a90",
"reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0"
},
"conflict": {
"phpstan/phpstan": "<1.11.8"
},
"require-dev": {
"phpstan/phpstan": "^1.11.8",
"phpstan/phpstan-strict-rules": "^1.1",
"phpunit/phpunit": "^8 || ^9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.x-dev"
},
"phpstan": {
"includes": [
"extension.neon"
]
}
},
"autoload": {
"psr-4": {
"Composer\\Pcre\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"description": "PCRE wrapping library that offers type-safe preg_* replacements.",
"keywords": [
"PCRE",
"preg",
"regex",
"regular expression"
],
"support": {
"issues": "https://github.com/composer/pcre/issues",
"source": "https://github.com/composer/pcre/tree/3.2.0"
},
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
],
"time": "2024-07-25T09:36:02+00:00"
},
{
"name": "dasprid/enum",
"version": "1.0.5",
@ -2740,6 +2819,191 @@
},
"time": "2022-04-15T14:02:14+00:00"
},
{
"name": "maennchen/zipstream-php",
"version": "3.1.2",
"source": {
"type": "git",
"url": "https://github.com/maennchen/ZipStream-PHP.git",
"reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/aeadcf5c412332eb426c0f9b4485f6accba2a99f",
"reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"ext-zlib": "*",
"php-64bit": "^8.2"
},
"require-dev": {
"brianium/paratest": "^7.7",
"ext-zip": "*",
"friendsofphp/php-cs-fixer": "^3.16",
"guzzlehttp/guzzle": "^7.5",
"mikey179/vfsstream": "^1.6",
"php-coveralls/php-coveralls": "^2.5",
"phpunit/phpunit": "^11.0",
"vimeo/psalm": "^6.0"
},
"suggest": {
"guzzlehttp/psr7": "^2.4",
"psr/http-message": "^2.0"
},
"type": "library",
"autoload": {
"psr-4": {
"ZipStream\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paul Duncan",
"email": "pabs@pablotron.org"
},
{
"name": "Jonatan Männchen",
"email": "jonatan@maennchen.ch"
},
{
"name": "Jesse Donat",
"email": "donatj@gmail.com"
},
{
"name": "András Kolesár",
"email": "kolesar@kolesar.hu"
}
],
"description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
"keywords": [
"stream",
"zip"
],
"support": {
"issues": "https://github.com/maennchen/ZipStream-PHP/issues",
"source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.2"
},
"funding": [
{
"url": "https://github.com/maennchen",
"type": "github"
}
],
"time": "2025-01-27T12:07:53+00:00"
},
{
"name": "markbaker/complex",
"version": "3.0.2",
"source": {
"type": "git",
"url": "https://github.com/MarkBaker/PHPComplex.git",
"reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
"reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "dev-master",
"phpcompatibility/php-compatibility": "^9.3",
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
"squizlabs/php_codesniffer": "^3.7"
},
"type": "library",
"autoload": {
"psr-4": {
"Complex\\": "classes/src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mark Baker",
"email": "mark@lange.demon.co.uk"
}
],
"description": "PHP Class for working with complex numbers",
"homepage": "https://github.com/MarkBaker/PHPComplex",
"keywords": [
"complex",
"mathematics"
],
"support": {
"issues": "https://github.com/MarkBaker/PHPComplex/issues",
"source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2"
},
"time": "2022-12-06T16:21:08+00:00"
},
{
"name": "markbaker/matrix",
"version": "3.0.1",
"source": {
"type": "git",
"url": "https://github.com/MarkBaker/PHPMatrix.git",
"reference": "728434227fe21be27ff6d86621a1b13107a2562c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c",
"reference": "728434227fe21be27ff6d86621a1b13107a2562c",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "dev-master",
"phpcompatibility/php-compatibility": "^9.3",
"phpdocumentor/phpdocumentor": "2.*",
"phploc/phploc": "^4.0",
"phpmd/phpmd": "2.*",
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
"sebastian/phpcpd": "^4.0",
"squizlabs/php_codesniffer": "^3.7"
},
"type": "library",
"autoload": {
"psr-4": {
"Matrix\\": "classes/src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mark Baker",
"email": "mark@demon-angel.eu"
}
],
"description": "PHP Class for working with matrices",
"homepage": "https://github.com/MarkBaker/PHPMatrix",
"keywords": [
"mathematics",
"matrix",
"vector"
],
"support": {
"issues": "https://github.com/MarkBaker/PHPMatrix/issues",
"source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1"
},
"time": "2022-12-02T22:17:43+00:00"
},
{
"name": "mobiledetect/mobiledetectlib",
"version": "2.8.45",
@ -3501,6 +3765,111 @@
],
"time": "2024-11-24T18:04:13+00:00"
},
{
"name": "phpoffice/phpspreadsheet",
"version": "4.1.0",
"source": {
"type": "git",
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
"reference": "6ff18c3a8df3a945492f75ce455d77f7ad55dd5c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/6ff18c3a8df3a945492f75ce455d77f7ad55dd5c",
"reference": "6ff18c3a8df3a945492f75ce455d77f7ad55dd5c",
"shasum": ""
},
"require": {
"composer/pcre": "^1||^2||^3",
"ext-ctype": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-gd": "*",
"ext-iconv": "*",
"ext-libxml": "*",
"ext-mbstring": "*",
"ext-simplexml": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"ext-xmlwriter": "*",
"ext-zip": "*",
"ext-zlib": "*",
"maennchen/zipstream-php": "^2.1 || ^3.0",
"markbaker/complex": "^3.0",
"markbaker/matrix": "^3.0",
"php": "^8.1",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.0",
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "dev-main",
"dompdf/dompdf": "^2.0 || ^3.0",
"friendsofphp/php-cs-fixer": "^3.2",
"mitoteam/jpgraph": "^10.3",
"mpdf/mpdf": "^8.1.1",
"phpcompatibility/php-compatibility": "^9.3",
"phpstan/phpstan": "^1.1",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^10.5",
"squizlabs/php_codesniffer": "^3.7",
"tecnickcom/tcpdf": "^6.5"
},
"suggest": {
"dompdf/dompdf": "Option for rendering PDF with PDF Writer",
"ext-intl": "PHP Internationalization Functions",
"mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
"mpdf/mpdf": "Option for rendering PDF with PDF Writer",
"tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
},
"type": "library",
"autoload": {
"psr-4": {
"PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Maarten Balliauw",
"homepage": "https://blog.maartenballiauw.be"
},
{
"name": "Mark Baker",
"homepage": "https://markbakeruk.net"
},
{
"name": "Franck Lefevre",
"homepage": "https://rootslabs.net"
},
{
"name": "Erik Tilt"
},
{
"name": "Adrien Crivelli"
}
],
"description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
"homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
"keywords": [
"OpenXML",
"excel",
"gnumeric",
"ods",
"php",
"spreadsheet",
"xls",
"xlsx"
],
"support": {
"issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
"source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/4.1.0"
},
"time": "2025-03-02T06:52:24+00:00"
},
{
"name": "phpoption/phpoption",
"version": "1.9.3",
@ -7873,85 +8242,6 @@
],
"time": "2024-06-12T14:13:04+00:00"
},
{
"name": "composer/pcre",
"version": "3.2.0",
"source": {
"type": "git",
"url": "https://github.com/composer/pcre.git",
"reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/pcre/zipball/ea4ab6f9580a4fd221e0418f2c357cdd39102a90",
"reference": "ea4ab6f9580a4fd221e0418f2c357cdd39102a90",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0"
},
"conflict": {
"phpstan/phpstan": "<1.11.8"
},
"require-dev": {
"phpstan/phpstan": "^1.11.8",
"phpstan/phpstan-strict-rules": "^1.1",
"phpunit/phpunit": "^8 || ^9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.x-dev"
},
"phpstan": {
"includes": [
"extension.neon"
]
}
},
"autoload": {
"psr-4": {
"Composer\\Pcre\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"description": "PCRE wrapping library that offers type-safe preg_* replacements.",
"keywords": [
"PCRE",
"preg",
"regex",
"regular expression"
],
"support": {
"issues": "https://github.com/composer/pcre/issues",
"source": "https://github.com/composer/pcre/tree/3.2.0"
},
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
],
"time": "2024-07-25T09:36:02+00:00"
},
{
"name": "doctrine/deprecations",
"version": "1.1.3",

View File

@ -19,30 +19,56 @@ if (!$template) {
header("Location: template_dashboard.php?status=error&message=" . urlencode("Template not found"));
exit;
}
// Debug del JSON
$clientSpecificFieldsJson = $template['client_specific_fields'] ?? '{}';
error_log("Raw client_specific_fields JSON: " . $clientSpecificFieldsJson);
$clientSpecificFields = json_decode($clientSpecificFieldsJson, true);
if (json_last_error() !== JSON_ERROR_NONE) {
error_log("JSON decode error: " . json_last_error_msg());
$clientSpecificFields = [];
} else {
error_log("Decoded client_specific_fields: " . print_r($clientSpecificFields, true));
}
?>
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!--favicon-->
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
<?php include('cssinclude.php'); ?>
<style>
.client-field-row .row {
margin-bottom: 0 !important;
display: flex;
align-items: center;
}
.client-field-row .col-md-1,
.client-field-row .col-md-2,
.client-field-row .col-md-3 {
padding: 0 5px;
overflow: hidden;
max-width: 100%;
flex: 0 0 auto;
}
.client-field-row input,
.client-field-row select {
width: 100%;
box-sizing: border-box;
}
</style>
<title>Edit Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
</head>
<body>
<!--wrapper-->
<div class="wrapper">
<!--sidebar wrapper -->
<?php include('include/navbar.php'); ?>
<!--end sidebar wrapper -->
<!--start header -->
<?php include('include/topbar.php'); ?>
<!--end header -->
<!--start page wrapper -->
<div class="page-wrapper">
<div class="page-content">
<div class="row row-cols-1 row-cols-md-2 row-cols-xl-4">
@ -55,8 +81,7 @@ if (!$template) {
<h4 class="my-1 text-info">4805</h4>
<p class="mb-0 font-13">+2.5% from last week</p>
</div>
<div class="widgets-icons-2 rounded-circle bg-gradient-blues text-white ms-auto"><i class='bx bxs-cart'></i>
</div>
<div class="widgets-icons-2 rounded-circle bg-gradient-blues text-white ms-auto"><i class='bx bxs-cart'></i></div>
</div>
</div>
</div>
@ -70,8 +95,7 @@ if (!$template) {
<h4 class="my-1 text-danger">$84,245</h4>
<p class="mb-0 font-13">+5.4% from last week</p>
</div>
<div class="widgets-icons-2 rounded-circle bg-gradient-burning text-white ms-auto"><i class='bx bxs-wallet'></i>
</div>
<div class="widgets-icons-2 rounded-circle bg-gradient-burning text-white ms-auto"><i class='bx bxs-wallet'></i></div>
</div>
</div>
</div>
@ -85,8 +109,7 @@ if (!$template) {
<h4 class="my-1 text-success">34.6%</h4>
<p class="mb-0 font-13">-4.5% from last week</p>
</div>
<div class="widgets-icons-2 rounded-circle bg-gradient-ohhappiness text-white ms-auto"><i class='bx bxs-bar-chart-alt-2'></i>
</div>
<div class="widgets-icons-2 rounded-circle bg-gradient-ohhappiness text-white ms-auto"><i class='bx bxs-bar-chart-alt-2'></i></div>
</div>
</div>
</div>
@ -100,15 +123,12 @@ if (!$template) {
<h4 class="my-1 text-warning">8.4K</h4>
<p class="mb-0 font-13">+8.4% from last week</p>
</div>
<div class="widgets-icons-2 rounded-circle bg-gradient-orange text-white ms-auto"><i class='bx bxs-group'></i>
</div>
<div class="widgets-icons-2 rounded-circle bg-gradient-orange text-white ms-auto"><i class='bx bxs-group'></i></div>
</div>
</div>
</div>
</div>
</div><!--end row-->
</div>
<div class="card radius-10">
<div class="card-header">
@ -116,12 +136,10 @@ if (!$template) {
<div>
<h6 class="mb-0">Edit Template: <?php echo htmlspecialchars($template['name']); ?></h6>
</div>
</div>
</div>
<div class="card-body">
<div class="col-12">
<!-- Form di modifica -->
<form id="editTemplateForm" method="POST">
<input type="hidden" name="id" value="<?php echo $template['id']; ?>">
@ -150,13 +168,76 @@ if (!$template) {
<input type="text" name="target_table" class="form-control" value="<?php echo htmlspecialchars($template['target_table']); ?>" required>
</div>
<!-- Sezione per i campi specifici del cliente -->
<div class="mb-3">
<label class="form-label">Client-Specific Fields</label>
<div id="clientSpecificFields">
<?php
$index = 0;
if (!empty($clientSpecificFields) && is_array($clientSpecificFields)) {
foreach ($clientSpecificFields as $fieldName => $fieldData) {
if (is_array($fieldData)) {
$type = $fieldData['type'] ?? 'text';
$possibleValues = implode(', ', $fieldData['possible_values'] ?? []);
$isRequired = isset($fieldData['is_required']) && $fieldData['is_required'] ? '1' : '0';
$exportColumnName = $fieldData['export_column_name'] ?? '';
$defaultValue = $fieldData['default_value'] ?? '';
error_log("Rendering field: $fieldName, type: $type, possible_values: $possibleValues, required: $isRequired, export: $exportColumnName, default: $defaultValue");
?>
<div class="client-field-row mb-2">
<div class="row align-items-center">
<div class="col-md-3">
<input type="text" name="specific_fields[<?php echo $index; ?>][name]" class="form-control" value="<?php echo htmlspecialchars($fieldName); ?>" placeholder="Field Name (e.g., SKU)">
</div>
<div class="col-md-2">
<select name="specific_fields[<?php echo $index; ?>][type]" class="form-control" onchange="toggleDropdownValues(this)">
<option value="text" <?php echo $type === 'text' ? 'selected' : ''; ?>>Text</option>
<option value="dropdown" <?php echo $type === 'dropdown' ? 'selected' : ''; ?>>Dropdown</option>
<option value="date" <?php echo $type === 'date' ? 'selected' : ''; ?>>Date</option>
<option value="boolean" <?php echo $type === 'boolean' ? 'selected' : ''; ?>>Yes/No</option>
</select>
</div>
<div class="col-md-2 dropdown-values" style="display: <?php echo $type === 'dropdown' ? 'block' : 'none'; ?>;">
<input type="text" name="specific_fields[<?php echo $index; ?>][possible_values]" class="form-control" value="<?php echo htmlspecialchars($possibleValues); ?>" placeholder="<?php echo !empty($fieldData['possible_values']) ? htmlspecialchars($possibleValues) : ''; ?>">
</div>
<div class="col-md-1">
<select name="specific_fields[<?php echo $index; ?>][required]" class="form-control">
<option value="1" <?php echo $isRequired === '1' ? 'selected' : ''; ?>>Yes</option>
<option value="0" <?php echo $isRequired === '0' ? 'selected' : ''; ?>>No</option>
</select>
</div>
<div class="col-md-2">
<input type="text" name="specific_fields[<?php echo $index; ?>][export_column_name]" class="form-control" value="<?php echo htmlspecialchars($exportColumnName); ?>" placeholder="Export Column Name (e.g., MONCLER_SKU)">
</div>
<div class="col-md-1">
<input type="text" name="specific_fields[<?php echo $index; ?>][default_value]" class="form-control" value="<?php echo htmlspecialchars($defaultValue); ?>" placeholder="Default Value (optional)">
</div>
<div class="col-md-1">
<button type="button" class="btn btn-danger remove-field">-</button>
</div>
</div>
</div>
<?php
$index++;
} else {
error_log("Invalid field data for field name: $fieldName, skipping...");
}
}
} else {
error_log("No client-specific fields found or invalid array for template ID: $id");
}
?>
</div>
<button type="button" class="btn btn-primary mt-2" id="addField">Add Field</button>
</div>
<br>
<button type="submit" class="btn btn-primary"><?= htmlspecialchars($savechanges, ENT_QUOTES, 'UTF-8'); ?></button>
<a href="templates_dashboard.php" class="btn btn-secondary">Cancel</a>
</form>
</div>
</div>
</div>
</div>
</div>
<!--end page wrapper -->
@ -170,57 +251,249 @@ if (!$template) {
</div>
<!--end wrapper-->
<!-- search modal -->
<?php //include('include/searchmodal.php');
?>
<!-- end search modal -->
<!--start switcher-->
<?php //include('include/themeswitcher.php');
?>
<!--end switcher-->
<?php include('jsinclude.php'); ?>
<!-- Temporaneamente disabilitato jsinclude.php per test -->
<!-- <?php include('jsinclude.php'); ?> -->
<script>
document.getElementById("editTemplateForm").addEventListener("submit", function(e) {
e.preventDefault(); // Previene il reload della pagina
document.addEventListener("DOMContentLoaded", function() {
const form = document.getElementById("editTemplateForm");
const addFieldButton = document.getElementById("addField");
const container = document.getElementById("clientSpecificFields");
let formData = new FormData(this);
if (!form || !addFieldButton || !container) {
console.error("One or more DOM elements not found:", {
form,
addFieldButton,
container
});
return;
}
fetch("process_edit_template_xls.php", {
method: "POST",
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
Swal.fire({
title: "Success!",
text: "Template updated successfully!",
icon: "success",
confirmButtonText: "OK"
}).then(() => {
window.location.href = "templates_dashboard.php";
console.log("All DOM elements found");
// Debug iniziale del DOM
const debugDom = () => {
const initialRows = container.getElementsByClassName("client-field-row");
console.log("Initial number of rows:", initialRows.length);
for (let i = 0; i < initialRows.length; i++) {
const inputs = initialRows[i].querySelectorAll("input, select");
const buttons = initialRows[i].querySelectorAll("button");
console.log(`Row ${i + 1} - Total inputs: ${inputs.length}, Total buttons: ${buttons.length}`);
inputs.forEach(input => console.log(`Input name: ${input.name}, value: ${input.value}, placeholder: ${input.placeholder}`));
buttons.forEach(button => console.log(`Button type: ${button.type}`));
}
};
debugDom();
// Pulizia del DOM da input extra
const cleanDom = () => {
const rows = container.getElementsByClassName("client-field-row");
for (let i = 0; i < rows.length; i++) {
const inputs = rows[i].querySelectorAll("input, select");
if (inputs.length > 6) { // 6 input/select attesi
console.warn(`Row ${i + 1}: Extra inputs detected, removing excess...`);
inputs.forEach((input, index) => {
if (index >= 6) {
console.log(`Removing extra input: ${input.name}`);
input.remove();
}
});
} else {
}
}
};
cleanDom();
// Osservatore del DOM per rilevare modifiche
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (mutation.addedNodes.length) {
console.log("DOM modified: New nodes added", mutation.addedNodes);
cleanDom(); // Pulisce il DOM ogni volta che viene modificato
}
});
});
observer.observe(container, {
childList: true,
subtree: true
});
// Gestione dinamica dei campi specifici
addFieldButton.addEventListener("click", function() {
console.log("Add Field button clicked");
const fieldCount = container.getElementsByClassName("client-field-row").length;
const newField = document.createElement("div");
newField.className = "client-field-row mb-2";
newField.innerHTML = `
<div class="row align-items-center">
<div class="col-md-3">
<input type="text" name="specific_fields[${fieldCount}][name]" class="form-control" placeholder="Field Name (e.g., SKU)">
</div>
<div class="col-md-2">
<select name="specific_fields[${fieldCount}][type]" class="form-control" onchange="toggleDropdownValues(this)">
<option value="text">Text</option>
<option value="dropdown">Dropdown</option>
<option value="date">Date</option>
<option value="boolean">Yes/No</option>
</select>
</div>
<div class="col-md-2 d-none dropdown-values" style="display: none;">
<input type="text" name="specific_fields[${fieldCount}][possible_values]" class="form-control" placeholder="">
</div>
<div class="col-md-1">
<select name="specific_fields[${fieldCount}][required]" class="form-control">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
</div>
<div class="col-md-2">
<input type="text" name="specific_fields[${fieldCount}][export_column_name]" class="form-control" placeholder="Export Column Name (e.g., MONCLER_SKU)">
</div>
<div class="col-md-1">
<input type="text" name="specific_fields[${fieldCount}][default_value]" class="form-control" placeholder="Default Value (optional)">
</div>
<div class="col-md-1">
<button type="button" class="btn btn-danger remove-field">-</button>
</div>
</div>
`;
container.appendChild(newField);
newField.querySelector(".remove-field").addEventListener("click", function() {
console.log("Remove Field button clicked");
container.removeChild(newField);
updateFieldIndices();
});
});
// Funzione per mostrare/nascondere il campo dei valori possibili per i dropdown
window.toggleDropdownValues = function(selectElement) {
console.log("Toggling dropdown values for:", selectElement.value);
const row = selectElement.closest(".row");
const dropdownValues = row.querySelector(".dropdown-values");
if (selectElement.value === "dropdown") {
dropdownValues.style.display = "block";
} else {
dropdownValues.style.display = "none";
}
};
// Event listener per i pulsanti di rimozione esistenti
document.querySelectorAll(".remove-field").forEach(button => {
button.addEventListener("click", function() {
console.log("Existing remove button clicked");
const container = document.getElementById("clientSpecificFields");
container.removeChild(button.closest(".client-field-row"));
updateFieldIndices();
});
});
// Funzione per aggiornare gli indici dei campi
function updateFieldIndices() {
console.log("Updating field indices");
const rows = container.getElementsByClassName("client-field-row");
for (let i = 0; i < rows.length; i++) {
const inputs = rows[i].querySelectorAll("input, select");
inputs.forEach(input => {
const name = input.name.replace(/\[\d+\]/, `[${i}]`);
input.name = name;
});
}
}
// Submit del form
form.addEventListener("submit", function(e) {
e.preventDefault();
console.log("Form submitted");
let formData = new FormData(this);
// Genera il JSON per client_specific_fields
let finalSpecificFields = {};
// Raccolta dei dati direttamente dal DOM
const fieldRows = container.getElementsByClassName("client-field-row");
for (let i = 0; i < fieldRows.length; i++) {
const row = fieldRows[i];
const inputs = row.querySelectorAll("input, select");
let fieldData = {};
inputs.forEach(input => {
const nameMatch = input.name.match(/specific_fields\[\d+\]\[(.*?)\]/);
if (nameMatch) {
const fieldName = nameMatch[1];
fieldData[fieldName] = input.value.trim();
}
});
if (fieldData.name) {
finalSpecificFields[fieldData.name] = {
type: fieldData.type || "text",
possible_values: (fieldData.possible_values && fieldData.type === "dropdown") ? fieldData.possible_values.split(",").map(v => v.trim()) : [],
is_required: fieldData.required === "1",
export_column_name: fieldData.export_column_name || "",
default_value: fieldData.default_value || ""
};
console.log(`Field ${fieldData.name}:`, finalSpecificFields[fieldData.name]);
}
}
console.log("Generated JSON for client_specific_fields:", JSON.stringify(finalSpecificFields));
// Aggiungi il JSON al FormData
formData.append("client_specific_fields", JSON.stringify(finalSpecificFields));
// Debug del FormData
console.log("FormData contents:");
for (let pair of formData.entries()) {
console.log(pair[0] + ': ' + pair[1]);
}
fetch("process_edit_template_xls.php", {
method: "POST",
body: formData
})
.then(response => response.json())
.then(data => {
console.log("Fetch response:", data);
if (data.success) {
Swal.fire({
title: "Success!",
text: "Template updated successfully!",
icon: "success",
confirmButtonText: "OK"
}).then(() => {
window.location.href = "templates_dashboard.php";
});
} else {
Swal.fire({
title: "Error!",
text: data.message,
icon: "error",
confirmButtonText: "OK"
});
}
})
.catch(error => {
console.error("Fetch error:", error);
Swal.fire({
title: "Error!",
text: data.message,
text: "An unexpected error occurred.",
icon: "error",
confirmButtonText: "OK"
});
}
})
.catch(error => {
Swal.fire({
title: "Error!",
text: "An unexpected error occurred.",
icon: "error",
confirmButtonText: "OK"
});
console.error(error);
});
});
});
</script>
</body>

View File

@ -0,0 +1,134 @@
<?php include('include/headscript.php'); ?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
<?php include('cssinclude.php'); ?>
<title>Template Buttons - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
<style>
/* Layout flessibile per gestire dimensioni diverse */
#templateButtons {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: flex-start;
/* Allinea a sinistra */
padding: 20px;
}
/* Definizione delle dimensioni */
.btn-small {
font-size: 12px;
padding: 6px 12px;
min-width: 100px;
min-height: 30px;
}
.btn-medium {
font-size: 16px;
padding: 10px 20px;
min-width: 130px;
min-height: 45px;
}
.btn-large {
font-size: 20px;
padding: 14px 28px;
min-width: 180px;
min-height: 60px;
}
/* Stile della barra di ricerca */
#searchInput {
width: 100%;
padding: 10px;
font-size: 16px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 5px;
}
</style>
</head>
<body>
<div class="wrapper">
<?php include('include/navbar.php'); ?>
<?php include('include/topbar.php'); ?>
<div class="page-wrapper">
<div class="page-content">
<?php include('top_stat_widget.php'); ?>
<div class="card radius-10">
<div class="card-header">
<h6 class="mb-0">Active Templates</h6>
</div>
<div class="card-body">
<!-- Barra di ricerca -->
<input type="text" id="searchInput" placeholder="Search template...">
<div id="templateButtons"></div>
</div>
</div>
</div>
</div>
<div class="overlay toggle-icon"></div>
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
<?php include('include/footer.php'); ?>
</div>
<?php include('jsinclude.php'); ?>
<script>
document.addEventListener("DOMContentLoaded", function() {
fetch("load_active_templates.php")
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error("Error loading templates:", data.message);
return;
}
let templateButtons = document.getElementById("templateButtons");
templateButtons.innerHTML = '';
data.data.forEach(template => {
let sizeClass = "btn-medium"; // Default
if (template.button_size === "small") sizeClass = "btn-small";
if (template.button_size === "large") sizeClass = "btn-large";
let btn = document.createElement("a");
btn.href = `import_xls.php?id=${template.id}`;
btn.className = `btn ${sizeClass}`;
btn.style.backgroundColor = template.button_bg_color;
btn.style.color = template.button_text_color;
btn.textContent = template.button_label;
btn.setAttribute("data-label", template.button_label.toLowerCase()); // Attributo per ricerca
templateButtons.appendChild(btn);
});
})
.catch(error => console.error("Fetch error:", error));
// Funzione per la ricerca live
document.getElementById("searchInput").addEventListener("input", function() {
let searchValue = this.value.toLowerCase();
document.querySelectorAll("#templateButtons a").forEach(btn => {
let label = btn.getAttribute("data-label");
if (label.includes(searchValue)) {
btn.style.display = "inline-block";
} else {
btn.style.display = "none";
}
});
});
});
</script>
</body>
</html>

View File

@ -0,0 +1,316 @@
<?php
include('include/headscript.php');
// Controlla se è stato passato un ID valido
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Invalid ID"));
exit;
}
$id = intval($_GET['id']); // Sanifica l'ID
// Recupera il template dal database
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$stmt = $pdo->prepare("SELECT * FROM excel_templates WHERE id = ?");
$stmt->execute([$id]);
$template = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$template) {
header("Location: template_dashboard.php?status=error&message=" . urlencode("Template not found"));
exit;
}
// Debug del template
error_log("Loaded template: " . print_r($template, true));
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
<?php include('cssinclude.php'); ?>
<style>
.table-container {
overflow-x: auto;
max-width: 100%;
margin-bottom: 20px;
}
.table {
width: 100%;
border-collapse: collapse;
}
.table th,
.table td {
padding: 10px;
text-align: left;
border: 1px solid #dee2e6;
min-width: 100px;
/* Larghezza minima per tutte le colonne */
max-width: 200px;
/* Larghezza massima iniziale */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.table th:first-child,
.table td:first-child {
min-width: 50px;
/* Colonna "Seleziona" più stretta */
max-width: 50px;
}
.table th {
background-color: #f8f9fa;
position: relative;
cursor: col-resize;
user-select: none;
}
/* Handle di ridimensionamento */
.table th .resize-handle {
position: absolute;
top: 0;
right: 0;
width: 5px;
height: 100%;
cursor: col-resize;
background: transparent;
}
.table th .resize-handle:hover {
background: #007bff;
}
.search-container {
margin-bottom: 20px;
}
.search-container input {
width: 300px;
padding: 8px;
border: 1px solid #ced4da;
border-radius: 5px;
}
.loader {
display: none;
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin: 10px auto;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
<title><?= htmlspecialchars($template['name']) ?> - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
</head>
<body>
<div class="wrapper">
<?php include('include/navbar.php'); ?>
<?php include('include/topbar.php'); ?>
<div class="page-wrapper">
<div class="page-content">
<?php include('top_stat_widget.php'); ?>
<div class="card radius-10">
<div class="card-header">
<div class="d-flex align-items-center">
<div>
<h6 class="mb-0"><?= htmlspecialchars($template['name']) ?></h6>
<small>Template ID: <?= $id ?>, Start Row: <?= $template['header_row'] ?>, Start Column: <?= $template['start_column'] ?></small>
</div>
</div>
</div>
<div class="card-body">
<!-- Form per caricare il file -->
<form id="uploadForm" enctype="multipart/form-data" class="mb-4">
<div class="mb-3">
<label for="excel_file" class="form-label">Carica il file Excel</label>
<input type="file" class="form-control" id="excel_file" name="excel_file" accept=".xls,.xlsx" required>
</div>
<button type="submit" class="btn btn-primary">Carica</button>
<div class="loader" id="loader"></div>
</form>
<!-- Contenitore per messaggi di errore -->
<div id="errorContainer" class="alert alert-danger mt-3" style="display: none;"></div>
<!-- Contenitore per la tabella -->
<div id="tableContainer"></div>
</div>
</div>
</div>
</div>
<!--end page wrapper -->
<div class="overlay toggle-icon"></div>
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
<?php include('include/footer.php'); ?>
</div>
<!--end wrapper-->
<!-- search modal -->
<?php //include('include/searchmodal.php');
?>
<!-- end search modal -->
<!--start switcher-->
<?php //include('include/themeswitcher.php');
?>
<!--end switcher-->
<?php include('jsinclude.php'); ?>
<script>
document.addEventListener("DOMContentLoaded", function() {
const form = document.getElementById('uploadForm');
const loader = document.getElementById('loader');
const errorContainer = document.getElementById('errorContainer');
const tableContainer = document.getElementById('tableContainer');
form.addEventListener('submit', function(e) {
e.preventDefault();
loader.style.display = 'block';
errorContainer.style.display = 'none';
tableContainer.innerHTML = '';
const formData = new FormData(this);
formData.append('template_id', <?= $id ?>);
formData.append('header_row', <?= $template['header_row'] ?>);
formData.append('start_column', <?= $template['start_column'] ?>);
fetch('process_import_xls.php', {
method: 'POST',
body: formData
})
.then(response => {
return response.text().then(text => {
console.log('Risposta grezza:', text);
try {
return JSON.parse(text);
} catch (e) {
throw new Error('Risposta non valida: ' + text);
}
});
})
.then(data => {
loader.style.display = 'none';
if (data.error) {
errorContainer.textContent = data.error;
errorContainer.style.display = 'block';
} else {
let html = `
<form id="selectRowsForm" action="import_edit.php" method="POST">
<input type="hidden" name="template_id" value="${data.template_id}">
<div class="search-container">
<input type="text" id="searchInput" class="form-control" placeholder="Cerca nelle righe...">
</div>
<div class="table-container">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Seleziona</th>
${data.columns.map(col => `<th>${col || 'Colonna senza nome'}<div class="resize-handle"></div></th>`).join('')}
</tr>
</thead>
<tbody>
${data.rows.map((row, index) => `
<tr>
<td><input type="checkbox" class="row-checkbox" name="selected_rows[]" value="${index}"></td>
${row.map(cell => `<td>${cell}</td>`).join('')}
</tr>
`).join('')}
</tbody>
</table>
</div>
<button type="submit" class="btn btn-primary mt-3" id="proceedButton" disabled>Prosegui</button>
</form>
`;
tableContainer.innerHTML = html;
// Aggiungi logica per il ridimensionamento delle colonne
const thElements = document.querySelectorAll('.table th');
thElements.forEach((th, index) => {
if (index === 0) return; // Escludi la colonna "Seleziona" dal ridimensionamento
const resizeHandle = th.querySelector('.resize-handle');
if (resizeHandle) {
resizeHandle.addEventListener('mousedown', (e) => {
e.preventDefault();
const startX = e.clientX;
const startWidth = th.offsetWidth;
const onMouseMove = (e) => {
const newWidth = Math.max(50, startWidth + (e.clientX - startX)); // Min 50px
th.style.width = `${newWidth}px`;
th.style.minWidth = `${newWidth}px`;
th.style.maxWidth = `${newWidth}px`;
// Aggiorna anche le celle della colonna corrispondente
const cells = document.querySelectorAll(`.table td:nth-child(${index + 1})`);
cells.forEach(cell => {
cell.style.width = `${newWidth}px`;
cell.style.minWidth = `${newWidth}px`;
cell.style.maxWidth = `${newWidth}px`;
});
};
const onMouseUp = () => {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
};
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
}
});
// Aggiungi event listener per la ricerca
const searchInput = document.getElementById('searchInput');
const rows = document.querySelectorAll('.table tbody tr');
const checkboxes = document.querySelectorAll('.row-checkbox');
const proceedButton = document.getElementById('proceedButton');
searchInput.addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
rows.forEach(row => {
const text = Array.from(row.cells).slice(1).map(cell => cell.textContent.toLowerCase()).join(' ');
row.style.display = text.includes(searchTerm) ? '' : 'none';
});
});
// Aggiungi event listener per i checkbox
checkboxes.forEach(checkbox => {
checkbox.addEventListener('change', function() {
proceedButton.disabled = !Array.from(checkboxes).some(cb => cb.checked);
});
});
}
})
.catch(error => {
loader.style.display = 'none';
errorContainer.textContent = 'Errore durante il caricamento del file: ' + error.message;
errorContainer.style.display = 'block';
});
});
});
</script>
</body>
</html>

View File

@ -87,15 +87,12 @@
</div>
</div><!--end row-->
<div class="card radius-10">
<div class="card-header">
<div class="d-flex align-items-center">
<div>
<h6 class="mb-0">Insert New XLS Template</h6>
</div>
</div>
</div>
<div class="card-body">
@ -125,6 +122,73 @@
<label class="form-label"><?= htmlspecialchars($desttable, ENT_QUOTES, 'UTF-8'); ?> *</label>
<input type="text" name="target_table" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Button Size</label>
<select name="button_size" class="form-control">
<option value="small">Small</option>
<option value="medium" selected>Medium</option>
<option value="large">Large</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">Button Background Color</label>
<input type="color" name="button_bg_color" class="form-control" value="#007bff">
</div>
<div class="mb-3">
<label class="form-label">Button Text Color</label>
<input type="color" name="button_text_color" class="form-control" value="#ffffff">
</div>
<div class="mb-3">
<label class="form-label">Button Label</label>
<input type="text" name="button_label" class="form-control" value="Click Me">
</div>
<!-- Nuova sezione per i campi specifici del cliente -->
<div class="mb-3">
<label class="form-label">Client-Specific Fields</label>
<div id="clientSpecificFields">
<!-- Contenitore per i campi dinamici -->
<div class="client-field-row mb-2">
<div class="row">
<div class="col-md-3">
<input type="text" name="specific_fields[0][name]" class="form-control" placeholder="Field Name (e.g., SKU)">
</div>
<div class="col-md-2">
<select name="specific_fields[0][type]" class="form-control">
<option value="text">Text</option>
<option value="dropdown">Dropdown</option>
<option value="date">Date</option>
<option value="boolean">Yes/No</option>
</select>
</div>
<div class="col-md-2 d-none dropdown-values" style="display: none;">
<input type="text" name="specific_fields[0][possible_values]" class="form-control" placeholder="Values (e.g., Red, Blue, Green)">
</div>
<div class="col-md-1">
<select name="specific_fields[0][required]" class="form-control">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
</div>
<div class="col-md-2">
<input type="text" name="specific_fields[0][export_column_name]" class="form-control" placeholder="Export Column Name (e.g., MONCLER_SKU)">
</div>
<div class="col-md-1">
<input type="text" name="specific_fields[0][default_value]" class="form-control" placeholder="Default Value (optional)">
</div>
<div class="col-md-1">
<button type="button" class="btn btn-danger remove-field">-</button>
</div>
</div>
</div>
</div>
<button type="button" class="btn btn-primary mt-2" id="addField">Add Field</button>
</div>
<br>
<button type="submit" class="btn btn-primary">Create Template</button>
<a href="templeates_dashboard.php" class="btn btn-secondary">Cancel</a>
@ -146,57 +210,200 @@
</div>
<!--end wrapper-->
<!-- search modal -->
<?php //include('include/searchmodal.php');
?>
<!-- end search modal -->
<!--start switcher-->
<?php //include('include/themeswitcher.php');
?>
<!--end switcher-->
<?php include('jsinclude.php'); ?>
<script>
document.getElementById("insertTemplateForm").addEventListener("submit", function(e) {
e.preventDefault(); // Previene il reload della pagina
// Debug iniziale
console.log("JavaScript is loaded and running!");
let formData = new FormData(this);
document.addEventListener("DOMContentLoaded", function() {
console.log("DOM is loaded");
fetch("process_insert_template_xls.php", {
method: "POST",
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
Swal.fire({
title: "Success!",
text: "Template created successfully!",
icon: "success",
confirmButtonText: "OK"
}).then(() => {
window.location.href = "list_templates.php";
});
} else {
const form = document.getElementById("insertTemplateForm");
const addFieldButton = document.getElementById("addField");
const container = document.getElementById("clientSpecificFields");
if (!form || !addFieldButton || !container) {
console.error("One or more DOM elements not found:", {
form,
addFieldButton,
container
});
return;
}
console.log("All DOM elements found");
// Gestione dinamica dei campi specifici
addFieldButton.addEventListener("click", function() {
console.log("Add Field button clicked");
const fieldCount = container.getElementsByClassName("client-field-row").length;
const newField = document.createElement("div");
newField.className = "client-field-row mb-2";
newField.innerHTML = `
<div class="row">
<div class="col-md-3">
<input type="text" name="specific_fields[${fieldCount}][name]" class="form-control" placeholder="Field Name (e.g., SKU)">
</div>
<div class="col-md-2">
<select name="specific_fields[${fieldCount}][type]" class="form-control" onchange="toggleDropdownValues(this)">
<option value="text">Text</option>
<option value="dropdown">Dropdown</option>
<option value="date">Date</option>
<option value="boolean">Yes/No</option>
</select>
</div>
<div class="col-md-2 d-none dropdown-values" style="display: none;">
<input type="text" name="specific_fields[${fieldCount}][possible_values]" class="form-control" placeholder="Values (e.g., Red, Blue, Green)">
</div>
<div class="col-md-1">
<select name="specific_fields[${fieldCount}][required]" class="form-control">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
</div>
<div class="col-md-2">
<input type="text" name="specific_fields[${fieldCount}][export_column_name]" class="form-control" placeholder="Export Column Name (e.g., MONCLER_SKU)">
</div>
<div class="col-md-1">
<input type="text" name="specific_fields[${fieldCount}][default_value]" class="form-control" placeholder="Default Value (optional)">
</div>
<div class="col-md-1">
<button type="button" class="btn btn-danger remove-field">-</button>
</div>
</div>
`;
container.appendChild(newField);
newField.querySelector(".remove-field").addEventListener("click", function() {
console.log("Remove Field button clicked");
container.removeChild(newField);
updateFieldIndices();
});
});
window.toggleDropdownValues = function(selectElement) {
console.log("Toggling dropdown values for:", selectElement.value);
const row = selectElement.closest(".row");
const dropdownValues = row.querySelector(".dropdown-values");
if (selectElement.value === "dropdown") {
dropdownValues.style.display = "block";
} else {
dropdownValues.style.display = "none";
}
};
document.querySelectorAll(".remove-field").forEach(button => {
button.addEventListener("click", function() {
console.log("Existing remove button clicked");
const container = document.getElementById("clientSpecificFields");
container.removeChild(button.closest(".client-field-row"));
updateFieldIndices();
});
});
function updateFieldIndices() {
console.log("Updating field indices");
const rows = container.getElementsByClassName("client-field-row");
for (let i = 0; i < rows.length; i++) {
const inputs = rows[i].querySelectorAll("input, select");
inputs.forEach(input => {
const name = input.name.replace(/\[\d+\]/, `[${i}]`);
input.name = name;
});
}
}
form.addEventListener("submit", function(e) {
e.preventDefault();
console.log("Form submitted");
let formData = new FormData(this);
// Genera il JSON per client_specific_fields
let finalSpecificFields = {};
// Raccolta dei dati direttamente dal DOM
const fieldRows = container.getElementsByClassName("client-field-row");
for (let i = 0; i < fieldRows.length; i++) {
const row = fieldRows[i];
const inputs = row.querySelectorAll("input, select");
let fieldData = {};
inputs.forEach(input => {
const nameMatch = input.name.match(/specific_fields\[\d+\]\[(.*?)\]/);
if (nameMatch) {
const fieldName = nameMatch[1];
fieldData[fieldName] = input.value.trim();
}
});
if (fieldData.name) {
finalSpecificFields[fieldData.name] = {
type: fieldData.type || "text",
possible_values: (fieldData.possible_values && fieldData.type === "dropdown") ? fieldData.possible_values.split(",").map(v => v.trim()) : [],
is_required: fieldData.required === "1",
export_column_name: fieldData.export_column_name || "",
default_value: fieldData.default_value || ""
};
console.log(`Field ${fieldData.name}:`, finalSpecificFields[fieldData.name]);
}
}
console.log("Generated JSON for client_specific_fields:", JSON.stringify(finalSpecificFields));
// Aggiungi il JSON al FormData
formData.append("client_specific_fields", JSON.stringify(finalSpecificFields));
// Debug del FormData
console.log("FormData contents:");
for (let pair of formData.entries()) {
console.log(pair[0] + ': ' + pair[1]);
}
fetch("process_insert_template_xls.php", {
method: "POST",
body: formData
})
.then(response => response.json())
.then(data => {
console.log("Fetch response:", data);
if (data.success) {
Swal.fire({
title: "Success!",
text: "Template created successfully!",
icon: "success",
confirmButtonText: "OK"
}).then(() => {
window.location.href = "templates_dashboard.php";
});
} else {
Swal.fire({
title: "Error!",
text: data.message,
icon: "error",
confirmButtonText: "OK"
});
}
})
.catch(error => {
console.error("Fetch error:", error);
Swal.fire({
title: "Error!",
text: data.message,
text: "An unexpected error occurred.",
icon: "error",
confirmButtonText: "OK"
});
}
})
.catch(error => {
Swal.fire({
title: "Error!",
text: "An unexpected error occurred.",
icon: "error",
confirmButtonText: "OK"
});
console.error(error);
});
});
});
</script>
</body>

View File

@ -0,0 +1,411 @@
<?php include('include/headscript.php'); ?>
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!--favicon-->
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
<?php include('cssinclude.php'); ?>
<title>Insert XLS Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
</head>
<body>
<!--wrapper-->
<div class="wrapper">
<!--sidebar wrapper -->
<?php include('include/navbar.php'); ?>
<!--end sidebar wrapper -->
<!--start header -->
<?php include('include/topbar.php'); ?>
<!--end header -->
<!--start page wrapper -->
<div class="page-wrapper">
<div class="page-content">
<div class="row row-cols-1 row-cols-md-2 row-cols-xl-4">
<div class="col">
<div class="card radius-10 border-start border-0 border-4 border-info">
<div class="card-body">
<div class="d-flex align-items-center">
<div>
<p class="mb-0 text-secondary">Total Orders</p>
<h4 class="my-1 text-info">4805</h4>
<p class="mb-0 font-13">+2.5% from last week</p>
</div>
<div class="widgets-icons-2 rounded-circle bg-gradient-blues text-white ms-auto"><i class='bx bxs-cart'></i>
</div>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card radius-10 border-start border-0 border-4 border-danger">
<div class="card-body">
<div class="d-flex align-items-center">
<div>
<p class="mb-0 text-secondary">Total Revenue</p>
<h4 class="my-1 text-danger">$84,245</h4>
<p class="mb-0 font-13">+5.4% from last week</p>
</div>
<div class="widgets-icons-2 rounded-circle bg-gradient-burning text-white ms-auto"><i class='bx bxs-wallet'></i>
</div>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card radius-10 border-start border-0 border-4 border-success">
<div class="card-body">
<div class="d-flex align-items-center">
<div>
<p class="mb-0 text-secondary">Bounce Rate</p>
<h4 class="my-1 text-success">34.6%</h4>
<p class="mb-0 font-13">-4.5% from last week</p>
</div>
<div class="widgets-icons-2 rounded-circle bg-gradient-ohhappiness text-white ms-auto"><i class='bx bxs-bar-chart-alt-2'></i>
</div>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card radius-10 border-start border-0 border-4 border-warning">
<div class="card-body">
<div class="d-flex align-items-center">
<div>
<p class="mb-0 text-secondary">Total Customers</p>
<h4 class="my-1 text-warning">8.4K</h4>
<p class="mb-0 font-13">+8.4% from last week</p>
</div>
<div class="widgets-icons-2 rounded-circle bg-gradient-orange text-white ms-auto"><i class='bx bxs-group'></i>
</div>
</div>
</div>
</div>
</div>
</div><!--end row-->
<div class="card radius-10">
<div class="card-header">
<div class="d-flex align-items-center">
<div>
<h6 class="mb-0">Insert New XLS Template</h6>
</div>
</div>
</div>
<div class="card-body">
<div class="col-12">
<form id="insertTemplateForm" method="POST">
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($templatename, ENT_QUOTES, 'UTF-8'); ?> *</label>
<input type="text" name="name" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($rowheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
<input type="number" name="header_row" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($columnheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
<input type="text" name="start_column" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($desctemplate, ENT_QUOTES, 'UTF-8'); ?></label>
<textarea name="description" class="form-control"></textarea>
</div>
<div class="mb-3">
<label class="form-label"><?= htmlspecialchars($desttable, ENT_QUOTES, 'UTF-8'); ?> *</label>
<input type="text" name="target_table" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Button Size</label>
<select name="button_size" class="form-control">
<option value="small">Small</option>
<option value="medium" selected>Medium</option>
<option value="large">Large</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">Button Background Color</label>
<input type="color" name="button_bg_color" class="form-control" value="#007bff">
</div>
<div class="mb-3">
<label class="form-label">Button Text Color</label>
<input type="color" name="button_text_color" class="form-control" value="#ffffff">
</div>
<div class="mb-3">
<label class="form-label">Button Label</label>
<input type="text" name="button_label" class="form-control" value="Click Me">
</div>
<!-- Nuova sezione per i campi specifici del cliente -->
<div class="mb-3">
<label class="form-label">Client-Specific Fields</label>
<div id="clientSpecificFields">
<!-- Contenitore per i campi dinamici -->
<div class="client-field-row mb-2">
<div class="row">
<div class="col-md-3">
<input type="text" name="specific_fields[0][name]" class="form-control" placeholder="Field Name (e.g., SKU)">
</div>
<div class="col-md-2">
<select name="specific_fields[0][type]" class="form-control">
<option value="text">Text</option>
<option value="dropdown">Dropdown</option>
<option value="date">Date</option>
<option value="boolean">Yes/No</option>
</select>
</div>
<div class="col-md-2 d-none dropdown-values" style="display: none;">
<input type="text" name="specific_fields[0][possible_values]" class="form-control" placeholder="Values (e.g., Red, Blue, Green)">
</div>
<div class="col-md-1">
<select name="specific_fields[0][required]" class="form-control">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
</div>
<div class="col-md-2">
<input type="text" name="specific_fields[0][export_column_name]" class="form-control" placeholder="Export Column Name (e.g., MONCLER_SKU)">
</div>
<div class="col-md-1">
<input type="text" name="specific_fields[0][default_value]" class="form-control" placeholder="Default Value (optional)">
</div>
<div class="col-md-1">
<button type="button" class="btn btn-danger remove-field">-</button>
</div>
</div>
</div>
</div>
<button type="button" class="btn btn-primary mt-2" id="addField">Add Field</button>
</div>
<br>
<button type="submit" class="btn btn-primary">Create Template</button>
<a href="templeates_dashboard.php" class="btn btn-secondary">Cancel</a>
</form>
</div>
</div>
</div>
</div>
</div>
<!--end page wrapper -->
<!--start overlay-->
<div class="overlay toggle-icon"></div>
<!--end overlay-->
<!--Start Back To Top Button-->
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
<!--End Back To Top Button-->
<?php include('include/footer.php'); ?>
</div>
<!--end wrapper-->
<!-- search modal -->
<?php //include('include/searchmodal.php');
?>
<!-- end search modal -->
<!--start switcher-->
<?php //include('include/themeswitcher.php');
?>
<!--end switcher-->
<?php include('jsinclude.php'); ?>
<script>
// Debug iniziale
console.log("JavaScript is loaded and running!");
document.addEventListener("DOMContentLoaded", function() {
console.log("DOM is loaded");
const form = document.getElementById("insertTemplateForm");
const addFieldButton = document.getElementById("addField");
const container = document.getElementById("clientSpecificFields");
if (!form || !addFieldButton || !container) {
console.error("One or more DOM elements not found:", {
form,
addFieldButton,
container
});
return;
}
console.log("All DOM elements found");
// Gestione dinamica dei campi specifici
addFieldButton.addEventListener("click", function() {
console.log("Add Field button clicked");
const fieldCount = container.getElementsByClassName("client-field-row").length;
const newField = document.createElement("div");
newField.className = "client-field-row mb-2";
newField.innerHTML = `
<div class="row">
<div class="col-md-3">
<input type="text" name="specific_fields[${fieldCount}][name]" class="form-control" placeholder="Field Name (e.g., SKU)">
</div>
<div class="col-md-2">
<select name="specific_fields[${fieldCount}][type]" class="form-control" onchange="toggleDropdownValues(this)">
<option value="text">Text</option>
<option value="dropdown">Dropdown</option>
<option value="date">Date</option>
<option value="boolean">Yes/No</option>
</select>
</div>
<div class="col-md-2 d-none dropdown-values" style="display: none;">
<input type="text" name="specific_fields[${fieldCount}][possible_values]" class="form-control" placeholder="Values (e.g., Red, Blue, Green)">
</div>
<div class="col-md-1">
<select name="specific_fields[${fieldCount}][required]" class="form-control">
<option value="1">Yes</option>
<option value="0">No</option>
</select>
</div>
<div class="col-md-2">
<input type="text" name="specific_fields[${fieldCount}][export_column_name]" class="form-control" placeholder="Export Column Name (e.g., MONCLER_SKU)">
</div>
<div class="col-md-1">
<input type="text" name="specific_fields[${fieldCount}][default_value]" class="form-control" placeholder="Default Value (optional)">
</div>
<div class="col-md-1">
<button type="button" class="btn btn-danger remove-field">-</button>
</div>
</div>
`;
container.appendChild(newField);
newField.querySelector(".remove-field").addEventListener("click", function() {
console.log("Remove Field button clicked");
container.removeChild(newField);
updateFieldIndices();
});
});
window.toggleDropdownValues = function(selectElement) {
console.log("Toggling dropdown values for:", selectElement.value);
const row = selectElement.closest(".row");
const dropdownValues = row.querySelector(".dropdown-values");
if (selectElement.value === "dropdown") {
dropdownValues.style.display = "block";
} else {
dropdownValues.style.display = "none";
}
};
document.querySelectorAll(".remove-field").forEach(button => {
button.addEventListener("click", function() {
console.log("Existing remove button clicked");
const container = document.getElementById("clientSpecificFields");
container.removeChild(button.closest(".client-field-row"));
updateFieldIndices();
});
});
function updateFieldIndices() {
console.log("Updating field indices");
const rows = container.getElementsByClassName("client-field-row");
for (let i = 0; i < rows.length; i++) {
const inputs = rows[i].querySelectorAll("input, select");
inputs.forEach(input => {
const name = input.name.replace(/\[\d+\]/, `[${i}]`);
input.name = name;
});
}
}
form.addEventListener("submit", function(e) {
e.preventDefault();
console.log("Form submitted");
let formData = new FormData(this);
// Genera il JSON per client_specific_fields
let finalSpecificFields = {};
// Raccolta dei dati direttamente dal DOM
const fieldRows = container.getElementsByClassName("client-field-row");
for (let i = 0; i < fieldRows.length; i++) {
const row = fieldRows[i];
const inputs = row.querySelectorAll("input, select");
let fieldData = {};
inputs.forEach(input => {
const nameMatch = input.name.match(/specific_fields\[\d+\]\[(.*?)\]/);
if (nameMatch) {
const fieldName = nameMatch[1];
fieldData[fieldName] = input.value.trim();
}
});
if (fieldData.name) {
finalSpecificFields[fieldData.name] = {
type: fieldData.type || "text",
possible_values: (fieldData.possible_values && fieldData.type === "dropdown") ? fieldData.possible_values.split(",").map(v => v.trim()) : [],
is_required: fieldData.required === "1",
export_column_name: fieldData.export_column_name || "",
default_value: fieldData.default_value || ""
};
console.log(`Field ${fieldData.name}:`, finalSpecificFields[fieldData.name]);
}
}
console.log("Generated JSON for client_specific_fields:", JSON.stringify(finalSpecificFields));
// Aggiungi il JSON al FormData
formData.append("client_specific_fields", JSON.stringify(finalSpecificFields));
// Debug del FormData
console.log("FormData contents:");
for (let pair of formData.entries()) {
console.log(pair[0] + ': ' + pair[1]);
}
fetch("process_insert_template_xls.php", {
method: "POST",
body: formData
})
.then(response => response.json())
.then(data => {
console.log("Fetch response:", data);
if (data.success) {
Swal.fire({
title: "Success!",
text: "Template created successfully!",
icon: "success",
confirmButtonText: "OK"
}).then(() => {
window.location.href = "templates_dashboard.php";
});
} else {
Swal.fire({
title: "Error!",
text: data.message,
icon: "error",
confirmButtonText: "OK"
});
}
})
.catch(error => {
console.error("Fetch error:", error);
Swal.fire({
title: "Error!",
text: "An unexpected error occurred.",
icon: "error",
confirmButtonText: "OK"
});
});
});
});
</script>
</body>
</html>

View File

@ -0,0 +1,27 @@
<?php
header('Content-Type: application/json');
require_once 'class/db-functions.php';
$response = ["success" => false, "data" => [], "message" => ""];
try {
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
if (!$pdo) {
throw new Exception('Database connection failed.');
}
// Recupera solo i template attivi
$stmt = $pdo->query("SELECT id, button_label, button_bg_color, button_text_color, button_size FROM excel_templates WHERE status = 'active'");
$templates = $stmt->fetchAll(PDO::FETCH_ASSOC);
$response["success"] = true;
$response["data"] = $templates;
} catch (PDOException $e) {
$response["message"] = "Database error: " . $e->getMessage();
} catch (Exception $e) {
$response["message"] = "Error: " . $e->getMessage();
}
echo json_encode($response);

View File

@ -0,0 +1,60 @@
<?php
require_once(__DIR__ . '/class/db-functions.php');
header('Content-Type: application/json');
if (!isset($_GET['template_id']) || !is_numeric($_GET['template_id'])) {
echo json_encode(["success" => false, "message" => "Invalid template ID"]);
exit;
}
$template_id = intval($_GET['template_id']);
try {
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// 1⃣ Recuperiamo il nome della tabella target da `excel_templates`
$stmt = $pdo->prepare("SELECT target_table FROM excel_templates WHERE id = ?");
$stmt->execute([$template_id]);
$template = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$template || empty($template['target_table'])) {
echo json_encode(["success" => false, "message" => "Template not found or missing target table"]);
exit;
}
$target_table = $template['target_table'];
// 2⃣ Recuperiamo le associazioni già esistenti per il template_id
$stmt = $pdo->prepare("SELECT excel_column, mysql_column, headerexcel FROM excel_column_mappings WHERE template_id = ?");
$stmt->execute([$template_id]);
$existing_mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Creiamo gli array delle colonne già mappate
$mapped_xls_columns = array_column($existing_mappings, 'excel_column');
$mapped_mysql_columns = array_column($existing_mappings, 'mysql_column'); // CORRETTO PER FILTRARE!
// 3⃣ Recuperiamo tutte le colonne disponibili nella tabella MySQL target
$stmt = $pdo->prepare("SHOW COLUMNS FROM `$target_table`");
$stmt->execute();
$table_columns = $stmt->fetchAll(PDO::FETCH_COLUMN);
// 🔥 FIX: Rimuoviamo le colonne MySQL che sono già state mappate!
$remaining_mysql_columns = array_values(array_diff($table_columns, $mapped_mysql_columns));
// 4⃣ Se abbiamo salvato gli header XLSX in `headerexcel`, li usiamo per calcolare le colonne XLSX non mappate
$headerexcel = !empty($existing_mappings) ? $existing_mappings[0]['headerexcel'] : '';
$all_xls_columns = !empty($headerexcel) ? explode(',', $headerexcel) : [];
$remaining_xls_columns = array_values(array_diff($all_xls_columns, $mapped_xls_columns));
// 5⃣ Invio dei dati al frontend
echo json_encode([
"success" => true,
"mappings" => $existing_mappings,
"remaining_xls_columns" => $remaining_xls_columns,
"remaining_mysql_columns" => $remaining_mysql_columns // 🔥 ORA LE COLONNE MYSQL SONO FILTRATE!
]);
} catch (PDOException $e) {
echo json_encode(["success" => false, "message" => "Database error: " . $e->getMessage()]);
}

View File

@ -7,10 +7,11 @@ if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
$id = intval($_GET['id']);
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$stmt = $pdo->prepare("SELECT name, header_row, start_column, target_table FROM excel_templates WHERE id = ?");
$stmt = $pdo->prepare("SELECT name, header_row, start_column, target_table, sample_xlsx FROM excel_templates WHERE id = ?");
$stmt->execute([$id]);
$template = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$template) {
die("Template not found");
}
@ -26,6 +27,8 @@ if (!$template) {
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
<?php include('cssinclude.php'); ?>
<title>Mapping XLS Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
</head>
<body>
@ -59,8 +62,20 @@ if (!$template) {
<div class="mb-4">
<label class="form-label">Upload XLS Example:</label>
<input type="file" id="xlsUpload" class="form-control">
<small id="uploadStatus" class="text-muted">
<?php if (!empty($template['sample_xlsx'])): ?>
Current file: <a href="xlstemplates/<?php echo htmlspecialchars($template['sample_xlsx']); ?>" target="_blank">
<?php echo htmlspecialchars($template['sample_xlsx']); ?>
</a>
<?php else: ?>
No file uploaded yet.
<?php endif; ?>
</small>
</div>
<!-- Association Section -->
<div class="row">
<div class="col-md-5">
@ -84,8 +99,9 @@ if (!$template) {
<!-- Save Button -->
<div class="mt-4 text-end">
<button class="btn btn-success" id="saveAssociations">Save Associations</button>
<a href="templates_dashboard.php" class="btn btn-primary"> Back to Template Dashboard</a>
</div>
</div>
</div>
@ -117,8 +133,48 @@ if (!$template) {
<script>
document.getElementById('xlsUpload').addEventListener('change', function(event) {
let file = event.target.files[0];
if (!file) return;
if (!file) {
console.error("❌ No file selected");
return;
}
console.log("📂 File selected:", file.name);
// 🔹 UPLOAD DEL FILE PRIMA DI PROCESSARE I DATI
let formData = new FormData();
formData.append("xls_file", file);
formData.append("template_id", <?php echo $id; ?>);
let statusText = document.getElementById('uploadStatus');
statusText.innerText = "Uploading...";
fetch('upload_xls_example.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error("❌ Error uploading file:", data.message);
statusText.innerText = "❌ Upload failed: " + data.message;
return;
}
console.log("✅ File uploaded successfully:", data.filepath);
statusText.innerHTML = `✅ File uploaded: <a href="xlstemplates/${data.filename}" target="_blank">${data.filename}</a>`;
// 🔹 UNA VOLTA CARICATO, ESTRAIAMO LE COLONNE
processXLSX(file);
})
.catch(error => {
console.error("❌ Fetch error:", error);
statusText.innerText = "❌ Upload failed. Check console.";
});
});
// 🔹 FUNZIONE PER PROCESSARE IL FILE ESTRARRE LE COLONNE
function processXLSX(file) {
let reader = new FileReader();
reader.onload = function(e) {
let data = new Uint8Array(e.target.result);
@ -127,21 +183,49 @@ if (!$template) {
});
let sheet = workbook.Sheets[workbook.SheetNames[0]];
console.log("📄 Sheet found:", workbook.SheetNames[0]);
// Recupera "Header Row" e "Start Column" dal template
let rowIndex = parseInt(document.getElementById('headerRow').textContent) || 1;
let startColumn = parseInt(document.getElementById('startColumn').textContent) || 1;
let headers = XLSX.utils.sheet_to_json(sheet, {
header: 1
})[rowIndex - 1];
console.log("📌 Using Header Row:", rowIndex, "Start Column:", startColumn);
if (!headers || headers.length === 0) {
console.error("No headers found in row:", rowIndex);
// Legge il foglio SENZA eliminare righe vuote
let sheetData = XLSX.utils.sheet_to_json(sheet, {
header: 1,
defval: "",
raw: false,
range: 0
});
console.log("📊 Sheet Data:", sheetData);
// Verifica che la riga richiesta esista
if (!sheetData[rowIndex - 1]) {
console.warn("⚠️ No headers found in the specified row:", rowIndex);
document.getElementById('xlsColumns').innerHTML = "<li class='list-group-item text-danger'>No headers found</li>";
return;
}
// Rimuove le colonne prima di startColumn
let adjustedHeaders = headers.slice(startColumn - 1);
// Prende la riga esatta specificata nel template
let headers = sheetData[rowIndex - 1] || [];
console.log("🔎 Raw Headers:", headers);
// Se la riga specificata è vuota, avvisa l'utente
if (!headers.length || headers.every(h => h.trim() === "")) {
console.warn("⚠️ Header row is empty at row:", rowIndex);
document.getElementById('xlsColumns').innerHTML = "<li class='list-group-item text-warning'>No headers found in the specified row</li>";
return;
}
// Rimuove le colonne prima di "Start Column"
let adjustedHeaders = headers.slice(startColumn - 1).filter(header => header !== undefined && header.trim() !== "");
console.log("✅ Extracted Headers:", adjustedHeaders);
// Popola la lista delle colonne XLSX
let xlsColumns = document.getElementById('xlsColumns');
xlsColumns.innerHTML = '';
@ -151,13 +235,22 @@ if (!$template) {
li.textContent = header;
xlsColumns.appendChild(li);
});
};
reader.readAsArrayBuffer(file);
});
}
document.addEventListener("DOMContentLoaded", function() {
fetch('load_table_columns.php?table=<?php echo urlencode($template['target_table']); ?>')
let selectedXLSColumn = null;
let selectedTableColumn = null;
let templateId = <?php echo $id; ?>;
/** =======================
* CARICAMENTO COLONNE MYSQL DAL DATABASE
* ======================= */
/* fetch('load_table_columns.php?table=<?php echo urlencode($template['target_table']); ?>')
.then(response => response.json())
.then(data => {
if (!data.success) {
@ -180,6 +273,174 @@ if (!$template) {
}
})
.catch(error => console.error('Error loading table columns:', error));
*/
/** =======================
* CARICAMENTO MAPPATURE GIÀ ESISTENTI
* ======================= */
function loadMappings() {
fetch('load_existing_mappings.php?template_id=' + templateId)
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error("❌ Error loading mappings:", data.message);
return;
}
let associationsList = document.getElementById('associationsList');
let xlsColumnsList = document.getElementById('xlsColumns');
let tableColumnsList = document.getElementById('tableColumns');
// 🔥 FIX: Pulisce sempre la lista prima di aggiornare
associationsList.innerHTML = '';
xlsColumnsList.innerHTML = '';
tableColumnsList.innerHTML = '';
// Carica le associazioni esistenti
data.mappings.forEach(mapping => {
let li = document.createElement('li');
li.className = 'list-group-item d-flex justify-content-between align-items-center';
li.innerHTML = `
${mapping.excel_column} ${mapping.mysql_column}
<button class="btn btn-danger btn-sm removeAssociation" data-excel="${mapping.excel_column}" data-mysql="${mapping.mysql_column}">X</button>
`;
associationsList.appendChild(li);
});
// 🔥 FIX: Ora carica SOLO le colonne MySQL rimaste disponibili!
if (Array.isArray(data.remaining_mysql_columns)) {
data.remaining_mysql_columns.forEach(column => {
let li = document.createElement('li');
li.className = 'list-group-item';
li.textContent = column;
tableColumnsList.appendChild(li);
});
}
// Carica le colonne XLSX rimanenti
if (Array.isArray(data.remaining_xls_columns)) {
data.remaining_xls_columns.forEach(header => {
let li = document.createElement('li');
li.className = 'list-group-item';
li.textContent = header;
xlsColumnsList.appendChild(li);
});
}
console.log("✅ Loaded existing mappings and remaining columns");
})
.catch(error => console.error("❌ Error fetching mappings:", error));
}
// 🔥 Chiama il caricamento iniziale
loadMappings();
/** =======================
* SELEZIONE COLONNE XLS & MYSQL
* ======================= */
document.getElementById('xlsColumns').addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
document.querySelectorAll('#xlsColumns li').forEach(el => el.classList.remove('active'));
event.target.classList.add('active');
selectedXLSColumn = event.target.textContent;
console.log("📌 Selected XLS Column:", selectedXLSColumn);
}
});
document.getElementById('tableColumns').addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
document.querySelectorAll('#tableColumns li').forEach(el => el.classList.remove('active'));
event.target.classList.add('active');
selectedTableColumn = event.target.textContent;
console.log("📌 Selected MySQL Column:", selectedTableColumn);
}
});
/** =======================
* AGGIUNTA ASSOCIAZIONE
* ======================= */
document.getElementById('addAssociation').addEventListener('click', function() {
if (!selectedXLSColumn || !selectedTableColumn) {
alert("Please select both an XLS and MySQL column.");
return;
}
// Salva l'associazione nel database
saveAssociation(selectedXLSColumn, selectedTableColumn);
// Reset selezioni
selectedXLSColumn = null;
selectedTableColumn = null;
});
function saveAssociation(excelColumn, mysqlColumn) {
let allHeaders = [...document.querySelectorAll('#xlsColumns li')].map(el => el.textContent).join(",");
fetch('save_column_mapping.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
template_id: templateId,
tablename: "<?php echo $template['target_table']; ?>",
excel_column: excelColumn,
mysql_column: mysqlColumn,
data_type: "VARCHAR",
is_required: 0,
default_value: "",
headerexcel: allHeaders
})
})
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error("❌ Error saving mapping:", data.message);
alert("Error saving mapping: " + data.message);
} else {
console.log("✅ Association saved:", data);
loadMappings(); // Ricarica le colonne dopo il salvataggio
}
})
.catch(error => console.error("❌ Fetch error:", error));
}
/** =======================
* RIMOZIONE ASSOCIAZIONE
* ======================= */
document.getElementById('associationsList').addEventListener('click', function(event) {
if (event.target.classList.contains('removeAssociation')) {
let excelColumn = event.target.getAttribute("data-excel");
let mysqlColumn = event.target.getAttribute("data-mysql");
removeAssociation(excelColumn, mysqlColumn);
}
});
function removeAssociation(excelColumn, mysqlColumn) {
fetch('remove_column_mapping.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
template_id: templateId,
excel_column: excelColumn,
mysql_column: mysqlColumn,
tablename: "<?php echo $template['target_table']; ?>"
})
})
.then(response => response.json())
.then(data => {
if (!data.success) {
console.error("❌ Error removing mapping:", data.message);
alert("Error removing mapping: " + data.message);
} else {
console.log("🗑️ Association removed:", data);
loadMappings(); // Ricarica la lista dopo la rimozione
}
})
.catch(error => console.error("❌ Fetch error:", error));
}
});
</script>
</body>

View File

@ -16,21 +16,28 @@ try {
$start_column = trim($_POST['start_column']);
$description = trim($_POST['description'] ?? '');
$target_table = trim($_POST['target_table']);
$client_specific_fields = trim($_POST['client_specific_fields'] ?? '{}'); // Recupera il JSON dei campi specifici
// Controllo sui campi obbligatori
if (empty($id) || empty($name) || empty($header_row) || empty($start_column) || empty($target_table)) {
throw new Exception("All fields marked with * are required.");
}
// Validazione opzionale del JSON (per sicurezza)
$decoded_fields = json_decode($client_specific_fields, true);
if (json_last_error() !== JSON_ERROR_NONE && $client_specific_fields !== '{}') {
throw new Exception("Invalid JSON format for client-specific fields.");
}
// Connessione al database
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// Aggiorna il database
// Aggiorna il database, includendo client_specific_fields
$stmt = $pdo->prepare("UPDATE excel_templates
SET name = ?, header_row = ?, start_column = ?, description = ?, target_table = ?, updated_at = NOW()
SET name = ?, header_row = ?, start_column = ?, description = ?, target_table = ?, client_specific_fields = ?, updated_at = NOW()
WHERE id = ?");
$stmt->execute([$name, $header_row, $start_column, $description, $target_table, $id]);
$stmt->execute([$name, $header_row, $start_column, $description, $target_table, $client_specific_fields, $id]);
if ($stmt->rowCount() > 0) {
$response["success"] = true;

View File

@ -0,0 +1,93 @@
<?php
// Sopprime eventuali output di errori (li logghiamo invece di mostrarli)
ob_start();
ini_set('display_errors', 1);
error_reporting(E_ALL);
// Inizia la sessione
session_start();
// Includi PHPSpreadsheet
require_once '../../vendor/autoload.php';
$response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0];
try {
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['excel_file'])) {
$template_id = isset($_POST['template_id']) ? intval($_POST['template_id']) : 0;
$header_row = isset($_POST['header_row']) ? intval($_POST['header_row']) : 1;
$start_column = isset($_POST['start_column']) ? intval($_POST['start_column']) : 1;
$file = $_FILES['excel_file'];
$fileError = $file['error'];
if ($fileError === UPLOAD_ERR_OK) {
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file['tmp_name']);
$worksheet = $spreadsheet->getActiveSheet();
$highestRow = $worksheet->getHighestRow();
$highestColumn = $worksheet->getHighestColumn();
$highestColumnIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestColumn);
$startRow = max(1, $header_row);
$startColumn = max(1, $start_column);
// Debug dei parametri
error_log("Processing - startRow: $startRow, startColumn: $startColumn, highestRow: $highestRow, highestColumn: $highestColumn, highestColumnIndex: $highestColumnIndex");
// Validazione degli indici
if ($startRow > $highestRow) {
$response['error'] = "La riga di partenza ($startRow) supera il numero totale di righe ($highestRow).";
} elseif ($startColumn > $highestColumnIndex) {
$response['error'] = "La colonna di partenza ($startColumn) supera il numero totale di colonne ($highestColumnIndex).";
} else {
$excelData = [];
// Estrai la riga degli header
$headerRowData = [];
for ($col = $startColumn; $col <= $highestColumnIndex; $col++) {
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
$cell = $worksheet->getCell($columnLetter . $header_row);
$cellValue = $cell ? $cell->getCalculatedValue() : ''; // Usa getCalculatedValue per le formule
$headerRowData[] = htmlspecialchars($cellValue ?: '');
}
// Estrai i dati a partire dalla riga successiva
for ($row = $startRow + 1; $row <= $highestRow; $row++) {
$rowData = [];
for ($col = $startColumn; $col <= $highestColumnIndex; $col++) {
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
$cell = $worksheet->getCell($columnLetter . $row);
$cellValue = $cell ? $cell->getCalculatedValue() : ''; // Usa getCalculatedValue per le formule
$rowData[] = htmlspecialchars($cellValue ?: '');
}
if (!empty(array_filter($rowData))) {
$excelData[] = $rowData;
}
}
// Salva i dati in sessione
$_SESSION['excel_data'] = $excelData;
$_SESSION['template_id'] = $template_id;
$_SESSION['headers'] = $headerRowData; // Salva gli header in sessione
$response['rows'] = $excelData;
$response['columns'] = $headerRowData; // Usa gli header reali
$response['template_id'] = $template_id;
}
} else {
$response['error'] = "Errore nell'upload del file: Codice errore $fileError.";
}
} else {
$response['error'] = "Richiesta non valida.";
}
} catch (Exception $e) {
$response['error'] = "Errore durante il caricamento del file: " . $e->getMessage();
error_log("Exception in process_import_xls.php: " . $e->getMessage());
}
// Pulisce qualsiasi output indesiderato
ob_end_clean();
// Invia la risposta JSON
header('Content-Type: application/json');
echo json_encode($response);
exit;

View File

@ -15,6 +15,19 @@ try {
$start_column = trim($_POST['start_column']);
$description = trim($_POST['description'] ?? '');
$target_table = trim($_POST['target_table']);
$button_size = trim($_POST['button_size'] ?? 'medium');
$button_bg_color = trim($_POST['button_bg_color'] ?? '#007bff');
$button_text_color = trim($_POST['button_text_color'] ?? '#ffffff');
$button_label = trim($_POST['button_label'] ?? 'Click Me');
$status = 'active'; // Default
// Recupera i client_specific_fields (JSON inviato dal form)
$client_specific_fields = trim($_POST['client_specific_fields'] ?? '{}');
// Decodifica il JSON per verificare che sia valido (opzionale, per sicurezza)
$decoded_fields = json_decode($client_specific_fields, true);
if (json_last_error() !== JSON_ERROR_NONE && !empty($client_specific_fields)) {
throw new Exception("Invalid JSON format for client-specific fields.");
}
// Controllo sui campi obbligatori
if (empty($name) || empty($header_row) || empty($start_column) || empty($target_table)) {
@ -25,10 +38,11 @@ try {
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// Inserisci nel database
$stmt = $pdo->prepare("INSERT INTO excel_templates (name, header_row, start_column, description, target_table, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, NOW(), NOW())");
$stmt->execute([$name, $header_row, $start_column, $description, $target_table]);
// Inserisci nel database, includendo client_specific_fields
$stmt = $pdo->prepare("INSERT INTO excel_templates
(name, header_row, start_column, description, target_table, button_size, button_bg_color, button_text_color, button_label, status, client_specific_fields, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())");
$stmt->execute([$name, $header_row, $start_column, $description, $target_table, $button_size, $button_bg_color, $button_text_color, $button_label, $status, $client_specific_fields]);
if ($stmt->rowCount() > 0) {
$response["success"] = true;

View File

@ -0,0 +1,62 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once(__DIR__ . '/class/db-functions.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$data = json_decode(file_get_contents("php://input"), true);
if (!isset($data['template_id'], $data['excel_column'], $data['mysql_column'], $data['tablename'])) {
echo json_encode(["success" => false, "message" => "Missing required fields"]);
exit;
}
// Rimuove l'associazione
$stmtDelete = $pdo->prepare("
DELETE FROM excel_column_mappings
WHERE template_id = ? AND excel_column = ? AND mysql_column = ? AND tablename = ?
");
$result = $stmtDelete->execute([
$data['template_id'],
$data['excel_column'],
$data['mysql_column'],
$data['tablename']
]);
if (!$result) {
echo json_encode(["success" => false, "message" => "Failed to delete mapping"]);
exit;
}
// Dopo la rimozione, aggiorna la lista delle colonne disponibili
$stmtColumns = $pdo->prepare("SHOW COLUMNS FROM " . $data['tablename']);
$stmtColumns->execute();
$all_mysql_columns = array_column($stmtColumns->fetchAll(PDO::FETCH_ASSOC), 'Field');
$stmtHeader = $pdo->prepare("SELECT headerexcel FROM excel_column_mappings WHERE template_id = ? LIMIT 1");
$stmtHeader->execute([$data['template_id']]);
$headerRow = $stmtHeader->fetch(PDO::FETCH_ASSOC);
$xls_headers = isset($headerRow['headerexcel']) ? explode(",", $headerRow['headerexcel']) : [];
// Ricalcola le colonne non associate
$stmtMappings = $pdo->prepare("SELECT excel_column, mysql_column FROM excel_column_mappings WHERE template_id = ?");
$stmtMappings->execute([$data['template_id']]);
$existingMappings = $stmtMappings->fetchAll(PDO::FETCH_ASSOC);
$mapped_xls_columns = array_column($existingMappings, 'excel_column');
$mapped_mysql_columns = array_column($existingMappings, 'mysql_column');
$remaining_xls_columns = array_diff($xls_headers, $mapped_xls_columns);
$remaining_mysql_columns = array_diff($all_mysql_columns, $mapped_mysql_columns);
echo json_encode([
"success" => true,
"remaining_xls_columns" => array_values($remaining_xls_columns),
"remaining_mysql_columns" => array_values($remaining_mysql_columns)
]);
exit;

View File

@ -0,0 +1,46 @@
<?php
header('Content-Type: application/json'); // Forza la risposta JSON
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once(__DIR__ . '/class/db-functions.php'); // Include la classe di connessione
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection(); // Ottieni la connessione
$data = json_decode(file_get_contents("php://input"), true);
if (!$data) {
echo json_encode(["success" => false, "message" => "Invalid JSON input"]);
exit;
}
if (!isset($data['template_id'], $data['tablename'], $data['excel_column'], $data['mysql_column'])) {
echo json_encode(["success" => false, "message" => "Missing required fields"]);
exit;
}
$stmt = $pdo->prepare("
INSERT INTO excel_column_mappings
(template_id, tablename, excel_column, mysql_column, data_type, is_required, default_value, headerexcel)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
");
$result = $stmt->execute([
$data['template_id'],
$data['tablename'],
$data['excel_column'],
$data['mysql_column'],
$data['data_type'],
$data['is_required'],
$data['default_value'],
$data['headerexcel']
]);
if (!$result) {
echo json_encode(["success" => false, "message" => "Database insert failed"]);
exit;
}
echo json_encode(["success" => true]);
exit;

View File

@ -11,6 +11,52 @@
<?php include('cssinclude.php'); ?>
<title>TRF-Project - Template Dashboard</title>
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
<style>
.switch {
position: relative;
display: inline-block;
width: 34px;
height: 20px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 34px;
}
.slider:before {
position: absolute;
content: "";
height: 12px;
width: 12px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked+.slider {
background-color: #4CAF50;
}
input:checked+.slider:before {
transform: translateX(14px);
}
</style>
</head>
<body>
@ -97,49 +143,71 @@
serverSide: false,
ajax: 'load_templates.php',
columns: [{
data: 'id'
data: 'name', // Nome del template
title: "Template Name"
},
{
data: 'name'
},
{
data: 'updated_at',
data: 'updated_at', // Ultima modifica, formattata come data leggibile
title: "Last Modified",
render: function(data) {
return new Date(data).toLocaleDateString();
}
},
{
data: 'header_row'
data: 'header_row', // Riga degli header
title: "Header Row"
},
{
data: 'start_column'
data: 'start_column', // Colonna di partenza
title: "Start Column"
},
{
data: 'description',
data: 'description', // Descrizione del template
title: "Description",
defaultContent: 'No description'
},
{
data: 'target_table'
data: 'target_table', // Tabella di destinazione
title: "Target Table"
},
{
data: 'id',
data: 'status', // Stato con Toggle Switch
title: "Status",
orderable: false,
searchable: false,
render: function(status, type, row) {
let checked = (status === "active") ? "checked" : "";
return `
<label class="switch">
<input type="checkbox" class="toggle-status" data-id="${row.id}" ${checked}>
<span class="slider round"></span>
</label>
`;
}
},
{
data: 'id', // Azioni: Modifica, Mappatura e Eliminazione
orderable: false,
searchable: false,
title: "Actions",
render: function(data) {
return `
<div class="d-flex">
<a href="edit_template_xls.php?id=${data}" class="btn btn-sm btn-primary me-1">
<i class="bx bx-edit-alt"></i>
</a>
<a href="mapping_template_xls.php?id=${data}" class="btn btn-sm btn-success me-1">
<i class="bx bx-link-alt"></i>
</a>
<button class="btn btn-sm btn-danger" onclick="confirmDelete(${data})">
<i class="bx bx-trash"></i>
</button>
</div>`;
<div class="d-flex">
<a href="edit_template_xls.php?id=${data}" class="btn btn-sm btn-primary me-1">
<i class="bx bx-edit-alt"></i>
</a>
<a href="mapping_template_xls.php?id=${data}" class="btn btn-sm btn-success me-1">
<i class="bx bx-link-alt"></i>
</a>
<button class="btn btn-sm btn-danger" onclick="confirmDelete(${data})">
<i class="bx bx-trash"></i>
</button>
</div>
`;
}
}
],
dom: '<"card-header border-bottom p-3"<"d-flex align-items-center"<"card-title mb-0 flex-grow-1"f>>>rt<"card-footer border-top p-3"<"d-flex align-items-center"<"me-auto"l><"d-flex gap-2"ip>>>',
lengthMenu: [10, 25, 50, 100],
@ -174,6 +242,32 @@
}
});
}
$(document).on("change", ".toggle-status", function() {
let templateId = $(this).data("id");
let newStatus = $(this).is(":checked") ? "active" : "inactive";
$.ajax({
url: "update_template_status.php",
type: "POST",
data: {
id: templateId,
status: newStatus
},
success: function(response) {
if (response.success) {
console.log("✅ Status updated successfully.");
} else {
console.error("❌ Error updating status:", response.message);
alert("Error updating status: " + response.message);
}
},
error: function() {
console.error("❌ AJAX error.");
}
});
});
</script>
</body>

View File

@ -0,0 +1,30 @@
<?php
header('Content-Type: application/json');
require_once 'class/db-functions.php';
$response = ["success" => false, "message" => ""];
try {
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
throw new Exception("Invalid request.");
}
$id = intval($_POST['id']);
$status = ($_POST['status'] === "active") ? "active" : "inactive";
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$stmt = $pdo->prepare("UPDATE excel_templates SET status = ?, updated_at = NOW() WHERE id = ?");
$stmt->execute([$status, $id]);
if ($stmt->rowCount() > 0) {
$response["success"] = true;
} else {
throw new Exception("Update failed.");
}
} catch (Exception $e) {
$response["message"] = $e->getMessage();
}
echo json_encode($response);

View File

@ -0,0 +1,48 @@
<?php
require_once(__DIR__ . '/class/db-functions.php');
header('Content-Type: application/json');
if (!isset($_POST['template_id']) || !is_numeric($_POST['template_id'])) {
echo json_encode(["success" => false, "message" => "Invalid template ID"]);
exit;
}
$template_id = intval($_POST['template_id']);
if (!isset($_FILES['xls_file']) || $_FILES['xls_file']['error'] !== UPLOAD_ERR_OK) {
echo json_encode(["success" => false, "message" => "File upload error"]);
exit;
}
$file = $_FILES['xls_file'];
$originalFilename = pathinfo($file['name'], PATHINFO_FILENAME);
$extension = pathinfo($file['name'], PATHINFO_EXTENSION);
// Crea il nuovo nome del file: {idtemplate}-{timestamp}-{nomeoriginale}.ext
$newFilename = $template_id . "-" . time() . "-" . preg_replace("/[^a-zA-Z0-9_-]/", "", $originalFilename) . "." . $extension;
$uploadDir = __DIR__ . '/xlstemplates/';
$uploadPath = $uploadDir . $newFilename;
// Assicura che la cartella esista
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
// Salva il file
if (!move_uploaded_file($file['tmp_name'], $uploadPath)) {
echo json_encode(["success" => false, "message" => "Failed to save file"]);
exit;
}
// Aggiorna il database con il nome del file
try {
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$stmt = $pdo->prepare("UPDATE excel_templates SET sample_xlsx = ? WHERE id = ?");
$stmt->execute([$newFilename, $template_id]);
echo json_encode(["success" => true, "filename" => $newFilename, "filepath" => "xlstemplates/" . $newFilename]);
} catch (PDOException $e) {
echo json_encode(["success" => false, "message" => "Database error: " . $e->getMessage()]);
}

Binary file not shown.