netgescon-day0/resources/views/admin/gescon-import/partials/stabili-dual-pane.blade.php

442 lines
30 KiB
PHP

<!-- Dual-pane Stabili import: Left (Legacy), Right (Current) -->
@php
$targetAdminId = $amministratore->id ?? null;
$targetAdminLabel = null;
if (isset($amministratore)) {
$label = trim($amministratore->denominazione_studio ?? '');
if ($label === '') {
$label = trim(($amministratore->nome ?? '') . ' ' . ($amministratore->cognome ?? ''));
}
$targetAdminLabel = $label !== '' ? $label : 'Amministratore #' . ($amministratore->id ?? '?');
}
@endphp
<div class="netgescon-card">
<div class="p-3 flex items-center justify-between">
<h3 class="text-lg font-semibold"><i class="mdi mdi-home-group mr-2"></i>Stabili: Legacy NetGescon</h3>
<div class="flex gap-2">
<a href="{{ route('admin.gescon-import.index', array_merge(request()->query(), ['refresh' => 1])) }}#stabili" class="netgescon-btn netgescon-btn-sm netgescon-netgescon-btn netgescon-btn-outline-secondary">
<i class="mdi mdi-refresh"></i> Ricarica
</a>
</div>
</div>
<div class="px-3 pb-3">
@if($targetAdminLabel)
<div class="bg-slate-100 border border-slate-200 rounded px-3 py-2 mb-3 text-xs text-slate-600">
Importazione destinata a: <strong>{{ $targetAdminLabel }}</strong>
@if(!empty($amministratore->codice_amministratore))
<span class="ml-2">(codice: {{ $amministratore->codice_amministratore }})</span>
@endif
</div>
@endif
<!-- Mini cards per mapping campi -->
<div class="grid md:grid-cols-2 gap-3 mb-3">
<div class="bg-slate-50 border border-slate-200 rounded p-3">
<div class="text-sm text-slate-600 mb-2"><i class="mdi mdi-connection mr-1"></i> Mappa campi Legacy NetGescon</div>
<div class="flex gap-2">
<a class="netgescon-btn netgescon-btn-sm netgescon-netgescon-btn netgescon-btn-outline-primary" href="#mapping" onclick="if(window.showGesconTab){ event.preventDefault(); showGesconTab('mapping'); }"><i class="mdi mdi-link-variant"></i> Apri Mapping</a>
@can('gescon-import.config')
<a class="netgescon-btn netgescon-btn-sm netgescon-netgescon-btn netgescon-btn-outline-secondary" href="<?= route('admin.gescon-import.config') ?>"><i class="mdi mdi-cog"></i> Configura</a>
@endcan
</div>
</div>
<div class="bg-slate-50 border border-slate-200 rounded p-3">
<div class="text-sm text-slate-600 mb-2"><i class="mdi mdi-database-search mr-1"></i> Anteprima archivio Legacy</div>
<?php if(empty($legacyPreview)): ?>
<div class="text-xs text-slate-500">Nessun dato disponibile.
<?php if(isset($hasMdbTools) && !$hasMdbTools): ?>
<span class="ml-1 text-amber-600">mdbtools non rilevato sul server.</span>
<?php else: ?>
<span class="ml-1">Verifica il percorso in Configurazione.</span>
<?php endif; ?>
</div>
<?php else: ?>
<div class="text-xs text-slate-600">Rilevati <?= count($legacyPreview) ?> codici legacy (Stabili.mdb o directory sorgente)</div>
<?php endif; ?>
<?php if(isset($legacyBasePath)): ?>
<div class="mt-2 text-[11px] text-slate-500 flex items-center gap-2 flex-wrap">
<div>
Cartella: <code class="bg-white border px-1 py-0.5 rounded"><?= e($legacyBasePath) ?></code>
<?php if(isset($hasMdbTools) && $hasMdbTools): ?>
<span class="ml-2">mdb-tools: <span class="netgescon-badge">ok</span></span>
<?php else: ?>
<span class="ml-2">mdb-tools: <span class="netgescon-badge netgescon-badge-secondary">non rilevato</span></span>
<?php endif; ?>
</div>
@can('gescon-import.config')
<form class="flex items-center gap-1" method="post" action="<?= route('admin.gescon-import.set-mdb') ?>">
<?= csrf_field() ?>
<span>File MDB:</span>
<input id="mdb_path_inline" name="mdb_path" type="text" value="<?= e(($mdbPath ?? '') ?: ($legacyBasePath.'/Stabili.mdb')) ?>" class="netgescon-input netgescon-input-sm" style="max-width: 460px;">
<button type="button" class="netgescon-btn netgescon-btn-sm netgescon-netgescon-btn netgescon-btn-outline-secondary" onclick="openMdbPicker()"><i class="mdi mdi-file-find-outline"></i></button>
<button type="submit" class="netgescon-btn netgescon-btn-sm netgescon-netgescon-btn netgescon-btn-outline-primary"><i class="mdi mdi-refresh"></i> Salva & Ricarica</button>
</form>
@else
<div class="text-slate-500">Percorso file MDB configurato dal Super Admin.</div>
@endcan
</div>
<?php endif; ?>
</div>
</div>
<div class="grid md:grid-cols-2 gap-3 items-start">
<!-- LEFT: Legacy list -->
<div>
<div class="border rounded h-full">
<div class="p-2 bg-slate-50 flex items-center justify-between border-b">
<div>
<strong><i class="mdi mdi-database-arrow-down me-1 text-primary"></i>Legacy (Stabili.mdb)</strong>
</div>
<small class="text-slate-500">Anteprima: <?= isset($legacyPreview) ? count($legacyPreview) : 0 ?></small>
</div>
<div class="p-2">
<div class="mb-2 flex gap-2">
<input class="netgescon-input netgescon-input-sm" placeholder="Cerca codice/denominazione" oninput="filterLegacy(this.value)">
@can('gescon-import.config')
<a class="netgescon-btn netgescon-btn-sm netgescon-netgescon-btn netgescon-btn-outline-secondary" href="<?= route('admin.gescon-import.config') ?>">
<i class="mdi mdi-cog"></i> Sorgente
</a>
@endcan
<?php if(!empty($legacyPreview)): ?>
<button class="netgescon-btn netgescon-btn-sm netgescon-netgescon-btn netgescon-btn-outline-primary" onclick="bulkDryRunAll()">
<i class="mdi mdi-flask-outline"></i> Dry-run tutti
</button>
<button class="netgescon-btn netgescon-btn-sm netgescon-netgescon-btn netgescon-btn-outline-success" onclick="bulkSyncAll()">
<i class="mdi mdi-sync"></i> Sync tutti
</button>
<?php endif; ?>
</div>
<div class="table-responsive" style="max-height: 420px; overflow:auto;">
<table class="table table-sm table-hover align-middle mb-0">
<thead class="table-secondary">
<tr>
<th style="width: 120px;">Legacy</th>
<th>Denominazione</th>
<th class="text-center" style="width: 160px;">Archivi</th>
<th class="text-center" style="width: 110px;">Sync</th>
<th style="width: 210px;">Azioni</th>
</tr>
</thead>
<tbody id="legacy-stabili-tbody">
<?php
$mapped = array_flip(($mappedLegacyCodes ?? []));
$syncBy = $syncByLegacy ?? [];
?>
<?php if(!empty($legacyPreview)): ?>
<?php // Prima: collegati in alto, poi da collegare
$sorted = $legacyPreview;
usort($sorted, function($a,$b) use($mapped){
$ma = isset($mapped[$a['legacy_code'] ?? '']);
$mb = isset($mapped[$b['legacy_code'] ?? '']);
if ($ma === $mb) return strnatcasecmp((string)($a['legacy_code']??''),(string)($b['legacy_code']??''));
return $ma ? -1 : 1;
});
?>
<?php $insertedBreak = false; $printedHeader = false; ?>
<?php foreach($sorted as $it): ?>
<?php $legacy = trim($it['legacy_code'] ?? ''); $den = trim($it['denominazione'] ?? ''); $hasGen = !empty($it['has_generale'] ?? null); $years = (int)($it['years_count'] ?? 0); ?>
<?php $isMapped = isset($mapped[$legacy]); ?>
<?php if(!$printedHeader && $isMapped): $printedHeader = true; ?>
<tr>
<td colspan="5" class="bg-slate-100 text-slate-600 text-xs uppercase tracking-wide">Collegate</td>
</tr>
<?php endif; ?>
<?php if(!$isMapped && !$insertedBreak): $insertedBreak = true; ?>
<tr>
<td colspan="5" class="bg-slate-100 text-slate-600 text-xs uppercase tracking-wide">Da collegare</td>
</tr>
<?php endif; ?>
<tr data-legacy="<?= e($legacy) ?>" data-denominazione="<?= e($den) ?>">
<td class="font-mono font-semibold"><?= e($legacy) ?></td>
<td><?= e($den ?: '—') ?></td>
<td class="text-center">
<?php if($hasGen): ?>
<span class="netgescon-badge netgescon-badge-success">GEN</span>
<?php else: ?>
<span class="netgescon-badge netgescon-badge-secondary">GEN</span>
<?php endif; ?>
<?php if($years>0): ?>
<span class="netgescon-badge netgescon-badge-primary">ANNI <?= $years ?></span>
<?php else: ?>
<span class="netgescon-badge netgescon-badge-secondary">ANNI 0</span>
<?php endif; ?>
</td>
<td class="text-center">
<?php $syncEnabled = (bool)($syncBy[$legacy] ?? false); ?>
<label class="text-xs text-slate-600">
<input type="checkbox" <?= $syncEnabled ? 'checked' : '' ?> onchange="toggleSyncFor('<?= e($legacy) ?>', this.checked)"> Abilita
</label>
</td>
<td>
<div class="btn-group btn-group-sm">
<form method="post" action="<?= route('admin.gescon-import.execute') ?>" onsubmit="return confirm('Eseguire DRY-RUN per <?= e($legacy) ?>?')">
<?= csrf_field() ?>
<input type="hidden" name="tipo_import" value="stabili">
<input type="hidden" name="legacy_code" value="<?= e($legacy) ?>">
<input type="hidden" name="legacy_denominazione" value="<?= e($den) ?>">
<?php if(isset($legacyBasePath)): ?>
<input type="hidden" name="path" value="<?= e($legacyBasePath) ?>">
<input type="hidden" name="mdb_path" value="<?= e(($mdbPath ?? '') ?: ($legacyBasePath.'/Stabili.mdb')) ?>">
<?php endif; ?>
<?php if($targetAdminId): ?>
<input type="hidden" name="amministratore_id" value="<?= e($targetAdminId) ?>">
<?php endif; ?>
<input type="hidden" name="test_mode" value="1">
<!-- Opzione avanzata: Forza mapping -->
<label class="ms-1 me-1 text-sm text-gray-500"><input type="checkbox" name="force_mapping" value="1"> Forza mapping</label>
<button class="netgescon-btn netgescon-netgescon-btn netgescon-btn-outline-primary"><i class="mdi mdi-flask"></i> Dry-run</button>
</form>
<form method="post" action="<?= route('admin.gescon-import.execute') ?>" onsubmit="return confirm('Sincronizzare definitivamente <?= e($legacy) ?>?')">
<?= csrf_field() ?>
<input type="hidden" name="tipo_import" value="stabili">
<input type="hidden" name="legacy_code" value="<?= e($legacy) ?>">
<input type="hidden" name="legacy_denominazione" value="<?= e($den) ?>">
<?php if(isset($legacyBasePath)): ?>
<input type="hidden" name="path" value="<?= e($legacyBasePath) ?>">
<input type="hidden" name="mdb_path" value="<?= e(($mdbPath ?? '') ?: ($legacyBasePath.'/Stabili.mdb')) ?>">
<?php endif; ?>
<?php if($targetAdminId): ?>
<input type="hidden" name="amministratore_id" value="<?= e($targetAdminId) ?>">
<?php endif; ?>
<input type="hidden" name="test_mode" value="0">
<!-- Il pulsante avvia la sincronizzazione, non l'import definitivo -->
<button class="netgescon-btn netgescon-netgescon-btn netgescon-btn-outline-success"><i class="mdi mdi-sync"></i> Sync</button>
</form>
<a class="netgescon-btn netgescon-netgescon-btn netgescon-btn-outline-secondary" href="<?= route('admin.gescon-import.index', ['legacy_code' => $legacy]) ?>#mapping">
<i class="mdi mdi-link-variant"></i> Mapping
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="4" class="text-center text-gray-500 py-3">
<i class="mdi mdi-information-outline"></i>
Nessun dato legacy disponibile. Imposta il percorso in Configurazione.
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- RIGHT: Current stabili list -->
<div>
<div class="border rounded h-full">
<div class="p-2 bg-slate-50 flex items-center justify-between border-b">
<div>
<strong><i class="mdi mdi-home-city me-1 text-success"></i>NetGescon (Corrente)</strong>
</div>
<small class="text-slate-500">Totali: <?= isset($stabili) ? number_format($stabili->total()) : 0 ?></small>
</div>
<div class="p-2">
<div class="table-responsive" style="max-height: 420px; overflow:auto;">
<table class="table table-sm table-hover align-middle mb-0">
<thead class="table-secondary">
<tr>
<th>Denominazione</th>
<th>Codice</th>
<th>CF</th>
<th>Provincia</th>
<th>Unità</th>
<th>Sync</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
<?php if(isset($stabili) && $stabili->count()): ?>
<?php foreach($stabili as $s): ?>
<tr>
<td><strong><?= e($s->denominazione) ?></strong></td>
<td class="font-mono"><?= e($s->codice_interno ?? $s->codice_stabile ?? '—') ?></td>
<td>
<div class="text-xs">
<div><span class="text-gray-500">Stabile:</span> <span class="font-mono"><?= e($s->codice_fiscale ?? '—') ?></span></div>
<?php if(property_exists($s, 'cod_fisc_amministratore') || isset($s->cod_fisc_amministratore)): ?>
<div><span class="text-gray-500">Amm.:</span> <span class="font-mono"><?= e($s->cod_fisc_amministratore ?? '—') ?></span></div>
<?php endif; ?>
</div>
</td>
<td><?= e($s->provincia ?? '—') ?></td>
<td class="text-center"><span class="netgescon-badge netgescon-badge-primary"><?= e($s->numero_unita ?? ($s->unitaImmobiliari()->count())) ?></span></td>
<td>
<?php
$isGescon = is_string($s->note ?? null) && stripos($s->note, 'GESCON') !== false;
$recent = false;
try { $recent = isset($s->updated_at) && $s->updated_at && $s->updated_at->gt(now()->subDays(7)); } catch (\Throwable $e) { $recent = false; }
?>
<?php if($isGescon): ?>
<span class="netgescon-badge <?= $recent ? 'netgescon-badge-success' : 'netgescon-badge-secondary' ?>">
<?= $recent ? 'agg.' : 'storico' ?>
</span>
<?php else: ?>
<span class="netgescon-badge netgescon-badge-secondary">—</span>
<?php endif; ?>
</td>
<td>
<div class="btn-group btn-group-sm">
<a class="netgescon-btn netgescon-btn-outline-info" href="/admin/stabili/<?= $s->id ?>">
<i class="mdi mdi-eye"></i>
</a>
<a class="netgescon-btn netgescon-netgescon-btn netgescon-btn-outline-warning" href="/admin/stabili/<?= $s->id ?>/edit">
<i class="mdi mdi-pencil"></i>
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="5" class="text-center text-gray-500 py-3">Nessuno stabile trovato</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php if(isset($stabili) && $stabili->hasPages()): ?>
<div class="mt-2 flex justify-end">
<?= $stabili->links() ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
function filterLegacy(term) {
term = (term || '').toLowerCase();
document.querySelectorAll('#legacy-stabili-tbody tr').forEach(tr => {
const legacy = (tr.getAttribute('data-legacy') || '').toLowerCase();
const den = (tr.getAttribute('data-denominazione') || '').toLowerCase();
tr.style.display = (legacy.includes(term) || den.includes(term)) ? '' : 'none';
});
}
</script>
<script>
async function toggleSyncFor(legacy, enabled){
try{
const fd = new FormData();
fd.append('_mapping_only','1');
fd.append('_legacy_code', legacy);
// preserviamo mapping esistente lato server; qui inviamo solo il flag di sincronizzazione
fd.append('sync_enabled', enabled ? '1' : '0');
await fetch('<?= route('admin.gescon-import.mapping.save') ?>',{ method:'POST', body: fd, headers:{'X-CSRF-TOKEN':'<?= csrf_token() ?>'} });
}catch(e){ console.error(e); alert('Errore salvataggio sincronizzazione'); }
}
async function bulkAction(all, testMode){
try{
const rows = Array.from(document.querySelectorAll('#legacy-stabili-tbody tr'));
for (const tr of rows){
if (tr.style.display==='none') continue; // rispetta filtro
const legacy = tr.getAttribute('data-legacy');
if (!legacy) continue;
// Se non è dry-run (quindi sync reale), rispetta il flag "Abilita"
if (!testMode){
const chk = tr.querySelector('input[type="checkbox"]');
if (!chk || !chk.checked) continue;
}
const form = document.createElement('form');
form.method='POST';
form.action='<?= route('admin.gescon-import.execute') ?>';
form.style.display='none';
form.innerHTML = `<?= csrf_field() ?>
<input type="hidden" name="tipo_import" value="stabili">
<input type="hidden" name="legacy_code" value="${legacy}">
<?php if(isset($legacyBasePath)): ?>
<input type="hidden" name="path" value="<?= e($legacyBasePath) ?>">
<input type="hidden" name="mdb_path" value="<?= e(($mdbPath ?? '') ?: ($legacyBasePath.'/Stabili.mdb')) ?>">
<?php endif; ?>
<?php if($targetAdminId): ?>
<input type="hidden" name="amministratore_id" value="<?= e($targetAdminId) ?>">
<?php endif; ?>
<input type="hidden" name="test_mode" value="${testMode?1:0}">
<input type="hidden" name="force_mapping" value="1">
`;
document.body.appendChild(form);
await fetch(form.action, { method:'POST', body: new FormData(form), headers:{'X-Requested-With':'XMLHttpRequest'} }).catch(()=>{});
form.remove();
}
location.reload();
}catch(e){ alert('Errore azione massiva'); }
}
function bulkDryRunAll(){ bulkAction(true, true); }
function bulkSyncAll(){ if(confirm('Confermi sincronizzazione per tutti gli stabili visibili?')) bulkAction(true, false); }
</script>
<!-- Minimal re-usable directory picker (files allowed) -->
<div id="mdbpicker" class="dirpicker-modal" style="display:none; position: fixed; inset: 0; background: rgba(0,0,0,.35); align-items: center; justify-content: center; z-index: 1050;">
<div class="dirpicker-dialog" style="width:min(780px,96vw); max-height:80vh; background:#fff; border-radius:.5rem; box-shadow:0 10px 30px rgba(0,0,0,.2); display:flex; flex-direction:column;">
<div class="dirpicker-header" style="padding:.75rem 1rem; border-bottom:1px solid #e5e7eb; display:flex; gap:.5rem; align-items:center;">
<strong><i class="mdi mdi-file-find-outline me-1"></i> Seleziona Stabili.mdb</strong>
<input id="mdbpicker-path" type="text" class="netgescon-input" onkeydown="if(event.key==='Enter'){ event.preventDefault(); loadMdbDir(this.value); }">
<button type="button" class="netgescon-btn netgescon-btn-sm netgescon-netgescon-btn netgescon-btn-outline-secondary" onclick="loadMdbDir(document.getElementById('mdbpicker-path').value)">Vai</button>
<button type="button" class="netgescon-btn netgescon-btn-sm netgescon-btn-outline-dark ms-auto" onclick="closeMdbPicker()">Chiudi</button>
</div>
<div id="mdbpicker-list" class="dirpicker-body" style="padding:.5rem 1rem; overflow:auto;"></div>
<div class="dirpicker-footer" style="padding:.75rem 1rem; border-top:1px solid #e5e7eb; display:flex; justify-content:space-between;">
<small class="text-gray-500">Radici consentite: /home, /var/www, /mnt</small>
<div>
<button type="button" class="netgescon-btn netgescon-netgescon-btn netgescon-btn-primary" onclick="selectMdbPath(document.getElementById('mdbpicker-path').value)">Seleziona percorso corrente</button>
</div>
</div>
</div>
</div>
<script>
function openMdbPicker(){
const modal = document.getElementById('mdbpicker');
modal.style.display='flex';
const start = document.getElementById('mdb_path_inline')?.value || '<?= e(($legacyBasePath ?? '/mnt/gescon-archives/gescon/dbc') ) ?>';
loadMdbDir(start);
}
function closeMdbPicker(){ document.getElementById('mdbpicker').style.display='none'; }
async function loadMdbDir(path){
const list = document.getElementById('mdbpicker-list');
list.innerHTML = '<div class="text-gray-500 p-2">Caricamento...</div>';
try {
const url = new URL('<?= route('admin.gescon-import.browse') ?>', window.location.origin);
url.searchParams.set('path', path);
const res = await fetch(url.toString(), { headers: { 'X-Requested-With': 'XMLHttpRequest' } });
const data = await res.json();
if (data.error) { list.innerHTML = '<div class="text-danger p-2">'+data.error+'</div>'; return; }
document.getElementById('mdbpicker-path').value = data.path;
const up = { name: '..', path: (data.path.replace(/\/$/, '').split('/').slice(0,-1).join('/') || '/'), is_dir: true };
const entries = [up].concat(data.entries||[]);
renderMdbEntries(entries);
} catch (e) { list.innerHTML = '<div class="text-danger p-2">Errore caricamento</div>'; }
}
function renderMdbEntries(entries){
const list = document.getElementById('mdbpicker-list');
list.innerHTML='';
entries.forEach(e => {
const entry = document.createElement('div');
entry.className = 'dir-entry';
entry.style = 'display:flex; align-items:center; gap:.5rem; padding:.35rem .25rem; border-radius:.25rem; cursor:pointer;';
entry.onmouseover = ()=> entry.style.background='#f3f4f6';
entry.onmouseout = ()=> entry.style.background='';
entry.innerHTML = '<span class="type" style="width:22px; text-align:center">'+(e.is_dir?'📁':'📄')+'</span><span class="name">'+e.name+'</span>';
entry.onclick = ()=> { if (e.is_dir) loadMdbDir(e.path); else selectMdbPath(e.path); };
list.appendChild(entry);
});
}
function selectMdbPath(p){ document.getElementById('mdb_path_inline').value = p; closeMdbPicker(); }
async function testMdbAndRescan(){
try {
const mdb = document.getElementById('mdb_path_inline').value;
if (!mdb) return alert('Seleziona un file MDB.');
// Simple redirect with refreshed cache and implicit new mdb path saved via POST if needed
const url = new URL('<?= route('admin.gescon-import.config') ?>', window.location.origin);
url.searchParams.set('refresh', '1');
// Use fetch to quickly check mdb-export can read headers, optional
window.location.href = url.toString();
} catch (e) { alert('Errore verifica: '+e.message); }
}
</script>