refactor solution to PHP through AI
This commit is contained in:
@@ -0,0 +1,439 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../common/ElixFormsComponent.php';
|
||||
require_once __DIR__ . '/../common/QueryParamHelper.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();
|
||||
Reference in New Issue
Block a user