import xls update
This commit is contained in:
parent
e9bad2260f
commit
8cad59e3d6
@ -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
450
composer.lock
generated
@ -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",
|
||||
|
||||
@ -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>
|
||||
|
||||
134
public/userarea/import_dashboard.php
Normal file
134
public/userarea/import_dashboard.php
Normal 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>
|
||||
316
public/userarea/import_xls.php
Normal file
316
public/userarea/import_xls.php
Normal 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>
|
||||
@ -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>
|
||||
|
||||
411
public/userarea/insert_template_xlsbck.php
Normal file
411
public/userarea/insert_template_xlsbck.php
Normal 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>
|
||||
27
public/userarea/load_active_templates.php
Normal file
27
public/userarea/load_active_templates.php
Normal 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);
|
||||
60
public/userarea/load_existing_mappings.php
Normal file
60
public/userarea/load_existing_mappings.php
Normal 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()]);
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
|
||||
93
public/userarea/process_import_xls.php
Normal file
93
public/userarea/process_import_xls.php
Normal 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;
|
||||
@ -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;
|
||||
|
||||
62
public/userarea/remove_column_mapping.php
Normal file
62
public/userarea/remove_column_mapping.php
Normal 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;
|
||||
46
public/userarea/save_column_mapping.php
Normal file
46
public/userarea/save_column_mapping.php
Normal 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;
|
||||
@ -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>
|
||||
|
||||
30
public/userarea/update_template_status.php
Normal file
30
public/userarea/update_template_status.php
Normal 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);
|
||||
48
public/userarea/upload_xls_example.php
Normal file
48
public/userarea/upload_xls_example.php
Normal 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()]);
|
||||
}
|
||||
BIN
public/userarea/xlstemplates/5-1740733446-row3.xlsx
Normal file
BIN
public/userarea/xlstemplates/5-1740733446-row3.xlsx
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user