439 lines
18 KiB
PHP
439 lines
18 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../../vendor/autoload.php';
|
|
|
|
use ElixForms\Common\ElixFormsComponent;
|
|
use ElixForms\Common\QueryParamHelper;
|
|
|
|
/**
|
|
* Pagina custom "Recupero Proposta CCT da Contratti" per elixForms.
|
|
* Equivalente PHP del componente React RecuperoPropostaCctDaContrattiComponent.
|
|
*/
|
|
class RecuperoPropostaCctDaContrattiPage extends ElixFormsComponent
|
|
{
|
|
private string $codiceFiscale;
|
|
private array $fieldKeys;
|
|
private int $minCharsForSearch;
|
|
|
|
public function __construct(array $properties, array $fieldKeys, int $minCharsForSearch = 3)
|
|
{
|
|
parent::__construct($properties);
|
|
$this->fieldKeys = $fieldKeys;
|
|
$this->minCharsForSearch = $minCharsForSearch;
|
|
|
|
// Recupera il codice fiscale dalla query string
|
|
$this->codiceFiscale = QueryParamHelper::getDecodedTextFromQuery($fieldKeys['codiceFiscale']) ?? '';
|
|
}
|
|
|
|
/**
|
|
* Genera l'HTML e CSS del componente Autocomplete e i relativi campi nascosti.
|
|
*/
|
|
protected function createCustomFormFields(): string
|
|
{
|
|
$escapedCodFis = htmlspecialchars($this->codiceFiscale, ENT_QUOTES, 'UTF-8');
|
|
$minChars = $this->minCharsForSearch;
|
|
|
|
// Campi di output definiti nella configurazione
|
|
$outCodiceContratto = htmlspecialchars($this->fieldKeys['codiceContratto']);
|
|
$outTitoloContratto = htmlspecialchars($this->fieldKeys['titoloContratto']);
|
|
$outIdDomanda = htmlspecialchars($this->fieldKeys['idDomanda']);
|
|
$outIdRicevuta = htmlspecialchars($this->fieldKeys['idRicevuta']);
|
|
|
|
// Recupero di eventuali valori correnti dal GET per precompilazione iniziale
|
|
$currCodice = htmlspecialchars(QueryParamHelper::getDecodedTextFromQuery($this->fieldKeys['codiceContratto']) ?? '', ENT_QUOTES, 'UTF-8');
|
|
$currTitolo = htmlspecialchars(QueryParamHelper::getDecodedTextFromQuery($this->fieldKeys['titoloContratto']) ?? '', ENT_QUOTES, 'UTF-8');
|
|
$currDomanda = htmlspecialchars(QueryParamHelper::getDecodedTextFromQuery($this->fieldKeys['idDomanda']) ?? '', ENT_QUOTES, 'UTF-8');
|
|
$currRicevuta = htmlspecialchars(QueryParamHelper::getDecodedTextFromQuery($this->fieldKeys['idRicevuta']) ?? '', ENT_QUOTES, 'UTF-8');
|
|
|
|
$initialSearchText = '';
|
|
if ($currCodice !== '' && $currTitolo !== '') {
|
|
$initialSearchText = "[{$currCodice}] {$currTitolo}";
|
|
}
|
|
|
|
// Layout del campo di ricerca
|
|
$autocompleteInputHtml = <<<HTML
|
|
<div class="autocomplete-wrapper" id="autocomplete-container">
|
|
<input
|
|
id="autocomplete-search"
|
|
name="autocomplete-search"
|
|
className="form-control"
|
|
class="form-control"
|
|
type="text"
|
|
placeholder="Digita almeno {$minChars} caratteri per cercare..."
|
|
value="{$initialSearchText}"
|
|
autoComplete="off"
|
|
/>
|
|
|
|
<div id="autocomplete-dropdown" class="autocomplete-dropdown d-none"></div>
|
|
|
|
<div id="selected-contract-card" class="card mt-3 d-none">
|
|
<div class="card-body">
|
|
<div class="row g-2">
|
|
<div class="col-12 col-md-6"><strong>ID Contratto:</strong> <span id="summary-id"></span></div>
|
|
<div class="col-12 col-md-6"><strong>Titolo:</strong> <span id="summary-title"></span></div>
|
|
<div class="col-12 col-md-6"><strong>ID Domanda:</strong> <span id="summary-domanda"></span></div>
|
|
<div class="col-12 col-md-6"><strong>ID Ricevuta:</strong> <span id="summary-ricevuta"></span></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Input Nascosti per elixForms -->
|
|
<input type="hidden" id="codiceContratto_hidden" name="{$outCodiceContratto}" value="{$currCodice}" />
|
|
<input type="hidden" id="titoloContratto_hidden" name="{$outTitoloContratto}" value="{$currTitolo}" />
|
|
<input type="hidden" id="idDomanda_hidden" name="{$outIdDomanda}" value="{$currDomanda}" />
|
|
<input type="hidden" id="idRicevuta_hidden" name="{$outIdRicevuta}" value="{$currRicevuta}" />
|
|
HTML;
|
|
|
|
$labelHtml = '<label class="form-label fw-semibold" htmlFor="autocomplete-search">Cerca Contratto</label>';
|
|
|
|
return (new \ElixForms\Common\ElixFormsElement($labelHtml, $autocompleteInputHtml))->render();
|
|
}
|
|
|
|
/**
|
|
* Inietta i CSS specifici per l'autocomplete e la card.
|
|
*/
|
|
protected function renderExtraContentPre(): string
|
|
{
|
|
ob_start();
|
|
?>
|
|
<style>
|
|
.autocomplete-wrapper {
|
|
position: relative;
|
|
width: 100%;
|
|
}
|
|
.autocomplete-dropdown {
|
|
position: absolute;
|
|
top: 100%;
|
|
left: 0;
|
|
right: 0;
|
|
z-index: 1000;
|
|
background: #ffffff;
|
|
border: 1px solid #a19f9d;
|
|
border-top: none;
|
|
border-radius: 0 0 2px 2px;
|
|
max-height: 250px;
|
|
overflow-y: auto;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
}
|
|
.autocomplete-dropdown-item {
|
|
padding: 8px 12px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
line-height: 1.4;
|
|
border-bottom: 1px solid #f3f2f1;
|
|
transition: background-color 0.1s ease;
|
|
}
|
|
.autocomplete-dropdown-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
.autocomplete-dropdown-item:hover,
|
|
.autocomplete-dropdown-item.highlighted {
|
|
background-color: #edebe9;
|
|
}
|
|
.autocomplete-dropdown-item .contract-id {
|
|
font-weight: 600;
|
|
color: #323130;
|
|
}
|
|
.autocomplete-dropdown-item .contract-title {
|
|
color: #605e5c;
|
|
}
|
|
.autocomplete-loading {
|
|
padding: 12px;
|
|
text-align: center;
|
|
color: #605e5c;
|
|
font-size: 14px;
|
|
font-style: italic;
|
|
}
|
|
.autocomplete-no-results {
|
|
padding: 12px;
|
|
text-align: center;
|
|
color: #a19f9d;
|
|
font-size: 14px;
|
|
}
|
|
.autocomplete-error {
|
|
padding: 12px;
|
|
text-align: center;
|
|
color: #a4262c;
|
|
font-size: 14px;
|
|
}
|
|
</style>
|
|
<?php
|
|
return ob_get_clean();
|
|
}
|
|
|
|
/**
|
|
* Inietta il codice JavaScript per gestire le chiamate AJAX al proxy ed il comportamento dell'interfaccia.
|
|
*/
|
|
protected function renderExtraContentPost(): string
|
|
{
|
|
$escapedCodFis = htmlspecialchars($this->codiceFiscale, ENT_QUOTES, 'UTF-8');
|
|
$minChars = $this->minCharsForSearch;
|
|
|
|
ob_start();
|
|
?>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
var searchInput = document.getElementById('autocomplete-search');
|
|
var dropdown = document.getElementById('autocomplete-dropdown');
|
|
var container = document.getElementById('autocomplete-container');
|
|
var card = document.getElementById('selected-contract-card');
|
|
|
|
var summaryId = document.getElementById('summary-id');
|
|
var summaryTitle = document.getElementById('summary-title');
|
|
var summaryDomanda = document.getElementById('summary-domanda');
|
|
var summaryRicevuta = document.getElementById('summary-ricevuta');
|
|
|
|
var hiddenCodice = document.getElementById('codiceContratto_hidden');
|
|
var hiddenTitolo = document.getElementById('titoloContratto_hidden');
|
|
var hiddenDomanda = document.getElementById('idDomanda_hidden');
|
|
var hiddenRicevuta = document.getElementById('idRicevuta_hidden');
|
|
|
|
var codFis = <?= json_encode($escapedCodFis) ?>;
|
|
var minChars = <?= $minChars ?>;
|
|
|
|
var searchResults = [];
|
|
var highlightedIndex = -1;
|
|
var debounceTimer = null;
|
|
|
|
// Se i campi hidden sono precompilati all'avvio, mostra la card di riepilogo
|
|
if (hiddenCodice.value && hiddenTitolo.value) {
|
|
showSummaryCard({
|
|
idContratto: hiddenCodice.value,
|
|
titoloContratto: hiddenTitolo.value,
|
|
idDomanda: hiddenDomanda.value,
|
|
idRicevuta: hiddenRicevuta.value
|
|
});
|
|
}
|
|
|
|
// Input event listener (con debounce)
|
|
searchInput.addEventListener('input', function() {
|
|
var text = this.value;
|
|
|
|
if (text.length === 0) {
|
|
clearSelection();
|
|
hideDropdown();
|
|
return;
|
|
}
|
|
|
|
if (debounceTimer) {
|
|
clearTimeout(debounceTimer);
|
|
}
|
|
|
|
if (text.length >= minChars) {
|
|
// Controlla se l'utente ha solo modificato il testo del contratto selezionato
|
|
if (hiddenCodice.value && text !== "[" + hiddenCodice.value + "] " + hiddenTitolo.value) {
|
|
clearSelection();
|
|
}
|
|
|
|
debounceTimer = setTimeout(function() {
|
|
fetchResults(text);
|
|
}, 300);
|
|
} else {
|
|
hideDropdown();
|
|
}
|
|
});
|
|
|
|
// Gestione eventi da tastiera
|
|
searchInput.addEventListener('keydown', function(event) {
|
|
if (dropdown.classList.contains('d-none')) return;
|
|
|
|
var items = dropdown.querySelectorAll('.autocomplete-dropdown-item');
|
|
if (items.length === 0) return;
|
|
|
|
switch (event.key) {
|
|
case 'ArrowDown':
|
|
event.preventDefault();
|
|
highlightedIndex = Math.min(highlightedIndex + 1, items.length - 1);
|
|
updateHighlight(items);
|
|
break;
|
|
case 'ArrowUp':
|
|
event.preventDefault();
|
|
highlightedIndex = Math.max(highlightedIndex - 1, 0);
|
|
updateHighlight(items);
|
|
break;
|
|
case 'Enter':
|
|
event.preventDefault();
|
|
if (highlightedIndex >= 0 && highlightedIndex < searchResults.length) {
|
|
selectContract(searchResults[highlightedIndex]);
|
|
}
|
|
break;
|
|
case 'Escape':
|
|
event.preventDefault();
|
|
hideDropdown();
|
|
break;
|
|
}
|
|
});
|
|
|
|
// Chiudi il menu a discesa se si clicca all'esterno
|
|
document.addEventListener('mousedown', function(event) {
|
|
if (!container.contains(event.target)) {
|
|
hideDropdown();
|
|
}
|
|
});
|
|
|
|
function fetchResults(term) {
|
|
showDropdown();
|
|
dropdown.innerHTML = '<div class="autocomplete-loading">Ricerca in corso...</div>';
|
|
highlightedIndex = -1;
|
|
|
|
// Calcolo dinamico della base directory per supportare URL con o senza slash finale
|
|
var baseDir = window.location.pathname;
|
|
if (!baseDir.endsWith('/') && !baseDir.endsWith('.php')) {
|
|
baseDir += '/';
|
|
}
|
|
var lastSlash = baseDir.lastIndexOf('/');
|
|
var dir = baseDir.substring(0, lastSlash + 1);
|
|
|
|
var url = dir + 'search-contratti.php?term=' + encodeURIComponent(term) + '&cod_fis=' + encodeURIComponent(codFis);
|
|
|
|
fetch(url)
|
|
.then(function(response) {
|
|
return response.text().then(function(text) {
|
|
var data = null;
|
|
try {
|
|
data = JSON.parse(text);
|
|
} catch (e) {
|
|
// Risposta non JSON
|
|
}
|
|
|
|
if (!response.ok) {
|
|
var errMsg = (data && data.message) ? data.message :
|
|
(data && data.error) ? data.error :
|
|
('Errore API: ' + response.status + ' ' + response.statusText);
|
|
throw new Error(errMsg);
|
|
}
|
|
|
|
if (data === null) {
|
|
throw new Error('Risposta del server non in formato JSON.');
|
|
}
|
|
|
|
return data;
|
|
});
|
|
})
|
|
.then(function(data) {
|
|
searchResults = data;
|
|
renderResults(data);
|
|
})
|
|
.catch(function(error) {
|
|
console.error('Errore durante la ricerca contratti:', error);
|
|
dropdown.innerHTML = '<div class="autocomplete-error">' + error.message + '</div>';
|
|
});
|
|
}
|
|
|
|
function renderResults(results) {
|
|
if (results.length === 0) {
|
|
dropdown.innerHTML = '<div class="autocomplete-no-results">Nessun risultato trovato</div>';
|
|
return;
|
|
}
|
|
|
|
dropdown.innerHTML = '';
|
|
results.forEach(function(contract, index) {
|
|
var item = document.createElement('div');
|
|
item.className = 'autocomplete-dropdown-item';
|
|
item.innerHTML = '<span class="contract-id">[' + contract.idContratto + ']</span> ' +
|
|
'<span class="contract-title">' + contract.titoloContratto + '</span>';
|
|
|
|
// Mouse interactions
|
|
item.addEventListener('mousedown', function(event) {
|
|
event.preventDefault();
|
|
selectContract(contract);
|
|
});
|
|
|
|
item.addEventListener('mouseenter', function() {
|
|
highlightedIndex = index;
|
|
var items = dropdown.querySelectorAll('.autocomplete-dropdown-item');
|
|
updateHighlight(items);
|
|
});
|
|
|
|
dropdown.appendChild(item);
|
|
});
|
|
}
|
|
|
|
function updateHighlight(items) {
|
|
items.forEach(function(item, index) {
|
|
if (index === highlightedIndex) {
|
|
item.classList.add('highlighted');
|
|
// Scorri il menu se l'elemento evidenziato è fuori vista
|
|
item.scrollIntoView({ block: 'nearest' });
|
|
} else {
|
|
item.classList.remove('highlighted');
|
|
}
|
|
});
|
|
}
|
|
|
|
function selectContract(contract) {
|
|
searchInput.value = '[' + contract.idContratto + '] ' + contract.titoloContratto;
|
|
|
|
hiddenCodice.value = contract.idContratto;
|
|
hiddenTitolo.value = contract.titoloContratto;
|
|
hiddenDomanda.value = contract.idDomanda;
|
|
hiddenRicevuta.value = contract.idRicevuta;
|
|
|
|
showSummaryCard(contract);
|
|
hideDropdown();
|
|
}
|
|
|
|
function showSummaryCard(contract) {
|
|
summaryId.textContent = contract.idContratto;
|
|
summaryTitle.textContent = contract.titoloContratto;
|
|
summaryDomanda.textContent = contract.idDomanda;
|
|
summaryRicevuta.textContent = contract.idRicevuta;
|
|
card.classList.remove('d-none');
|
|
}
|
|
|
|
function clearSelection() {
|
|
hiddenCodice.value = '';
|
|
hiddenTitolo.value = '';
|
|
hiddenDomanda.value = '';
|
|
hiddenRicevuta.value = '';
|
|
card.classList.add('d-none');
|
|
}
|
|
|
|
function showDropdown() {
|
|
dropdown.classList.remove('d-none');
|
|
}
|
|
|
|
function hideDropdown() {
|
|
dropdown.classList.add('d-none');
|
|
highlightedIndex = -1;
|
|
}
|
|
});
|
|
</script>
|
|
<?php
|
|
return ob_get_clean();
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// Entry Point della Pagina
|
|
// =============================================================================
|
|
|
|
// Lettura e validazione della configurazione
|
|
$configFile = __DIR__ . '/config.json';
|
|
if (!file_exists($configFile)) {
|
|
die("File di configurazione config.json non trovato.");
|
|
}
|
|
|
|
$config = json_decode(file_get_contents($configFile), true);
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
die("Errore di sintassi nel file config.json.");
|
|
}
|
|
|
|
// Istanziazione ed esecuzione del rendering
|
|
$page = new RecuperoPropostaCctDaContrattiPage(
|
|
$config['component'],
|
|
[
|
|
'codiceFiscale' => $config['elixForms']['inputs']['codiceFiscale'],
|
|
'idDomanda' => $config['elixForms']['outputs']['idDomanda'],
|
|
'idRicevuta' => $config['elixForms']['outputs']['idRicevuta'],
|
|
'codiceContratto'=> $config['elixForms']['outputs']['codiceContratto'],
|
|
'titoloContratto'=> $config['elixForms']['outputs']['titoloContratto'],
|
|
],
|
|
$config['component']['minCharsForSearch'] ?? 3
|
|
);
|
|
|
|
echo $page->render();
|