Compare commits
2 Commits
dba907a85f
...
772a077e01
| Author | SHA1 | Date | |
|---|---|---|---|
| 772a077e01 | |||
| c079737b5b |
|
|
@ -5,6 +5,11 @@ # Changelog
|
|||
## [Unreleased]
|
||||
|
||||
- Initial open-source release prep.
|
||||
- Reworked Supporto > Aggiornamento Nodo to show local/remote commit dates, explicit pending commit counts, and a clear note that Git updates do not move storage archives or private uploaded data.
|
||||
- Reorganized the Importazione Archivi operational tab so the action buttons come first, followed by the explanatory blocks and command fields.
|
||||
- Removed active-stabile filtering from topbar avvisi/urgenze counters and from the Google ticket-operativi box so cross-stabile operational tickets stay visible.
|
||||
- Removed the Google Workspace widget from the main/support dashboard, leaving the full panel only under Impostazioni > Google.
|
||||
- Tightened Filament sidebar navigation spacing between main menu groups for denser top-level navigation.
|
||||
- Added immediate local preview for Ticket Mobile photos and attachments before final send on phone.
|
||||
- Split insurance handling out of Ticket Gestione into dedicated Tab 4 and added centralized Supporto claims list.
|
||||
- Fixed supplier ticket-detail access by resolving both intervention-id and ticket-id safely in supplier context.
|
||||
|
|
|
|||
|
|
@ -85,12 +85,20 @@ class Modifiche extends Page
|
|||
|
||||
public ?string $gitCurrentCommit = null;
|
||||
|
||||
public ?string $gitCurrentCommitDate = null;
|
||||
|
||||
public ?string $gitRemoteCommit = null;
|
||||
|
||||
public ?string $gitRemoteCommitDate = null;
|
||||
|
||||
public bool $gitWorkingTreeDirty = false;
|
||||
|
||||
public string $gitAheadBehind = '-';
|
||||
|
||||
public int $gitAheadCount = 0;
|
||||
|
||||
public int $gitBehindCount = 0;
|
||||
|
||||
public bool $gitSyncInProgress = false;
|
||||
|
||||
public ?string $gitSyncJobId = null;
|
||||
|
|
@ -1051,12 +1059,18 @@ private function loadGitWorkspaceStatus(): void
|
|||
{
|
||||
$this->gitCurrentBranch = $this->runGitProcess(['rev-parse', '--abbrev-ref', 'HEAD']);
|
||||
$this->gitCurrentCommit = $this->runGitProcess(['rev-parse', '--short', 'HEAD']);
|
||||
$this->gitCurrentCommitDate = $this->runGitProcess(['show', '-s', '--date=format:%d/%m/%Y %H:%M', '--format=%cd', 'HEAD']);
|
||||
$this->gitRemoteCommit = $this->runGitProcess(['rev-parse', '--short', 'refs/remotes/' . $this->gitRemote . '/' . $this->gitBranch]);
|
||||
$this->gitRemoteCommitDate = $this->runGitProcess(['show', '-s', '--date=format:%d/%m/%Y %H:%M', '--format=%cd', 'refs/remotes/' . $this->gitRemote . '/' . $this->gitBranch]);
|
||||
$this->gitWorkingTreeDirty = trim((string) ($this->runGitProcess(['status', '--porcelain']) ?? '')) !== '';
|
||||
$this->gitAheadCount = 0;
|
||||
$this->gitBehindCount = 0;
|
||||
|
||||
$aheadBehind = $this->runGitProcess(['rev-list', '--left-right', '--count', 'HEAD...refs/remotes/' . $this->gitRemote . '/' . $this->gitBranch]);
|
||||
if (is_string($aheadBehind) && preg_match('/^(\d+)\s+(\d+)$/', trim($aheadBehind), $matches) === 1) {
|
||||
$this->gitAheadBehind = 'ahead ' . $matches[1] . ' / behind ' . $matches[2];
|
||||
$this->gitAheadCount = (int) $matches[1];
|
||||
$this->gitBehindCount = (int) $matches[2];
|
||||
$this->gitAheadBehind = 'ahead ' . $this->gitAheadCount . ' / behind ' . $this->gitBehindCount;
|
||||
} else {
|
||||
$this->gitAheadBehind = '-';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,6 +114,9 @@ class TicketGestione extends Page
|
|||
/** @var array<string,mixed>|null */
|
||||
public ?array $attachmentPreview = null;
|
||||
|
||||
/** @var array<string,mixed>|null */
|
||||
public ?array $attachmentMapPreview = null;
|
||||
|
||||
/** @var \Illuminate\Support\Collection<int, Ticket> */
|
||||
public $tickets;
|
||||
|
||||
|
|
@ -320,6 +323,7 @@ public function openAttachmentPreview(int $attachmentId): void
|
|||
|
||||
$isImage = $attachment->isImage();
|
||||
$isPdf = $attachment->isPdf();
|
||||
$details = $this->getAttachmentDetails($attachment);
|
||||
|
||||
$this->attachmentPreview = [
|
||||
'id' => (int) $attachment->id,
|
||||
|
|
@ -329,12 +333,58 @@ public function openAttachmentPreview(int $attachmentId): void
|
|||
'is_image' => $isImage,
|
||||
'is_pdf' => $isPdf,
|
||||
'mime' => $mime,
|
||||
'details' => $details,
|
||||
];
|
||||
}
|
||||
|
||||
public function openAttachmentMapPreview(int $attachmentId): void
|
||||
{
|
||||
$ticket = $this->selectedTicket;
|
||||
if (! $ticket) {
|
||||
return;
|
||||
}
|
||||
|
||||
$attachment = $ticket->attachments->firstWhere('id', $attachmentId);
|
||||
if (! $attachment instanceof TicketAttachment) {
|
||||
return;
|
||||
}
|
||||
|
||||
$details = $this->getAttachmentDetails($attachment);
|
||||
$gps = $details['gps'] ?? [];
|
||||
$lat = $gps['lat'] ?? null;
|
||||
$lng = $gps['lng'] ?? null;
|
||||
|
||||
if (! is_numeric($lat) || ! is_numeric($lng)) {
|
||||
Notification::make()->title('Coordinate GPS non disponibili per questo allegato')->warning()->send();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->attachmentMapPreview = [
|
||||
'attachment_id' => (int) $attachment->id,
|
||||
'name' => (string) ($attachment->original_file_name ?? 'Allegato'),
|
||||
'label' => (string) ($gps['label'] ?? (number_format((float) $lat, 5, '.', '') . ', ' . number_format((float) $lng, 5, '.', ''))),
|
||||
'maps_url' => (string) ($details['maps_url'] ?? ('https://maps.google.com/?q=' . $lat . ',' . $lng)),
|
||||
'embed_url' => (string) ($details['maps_embed_url'] ?? ('https://maps.google.com/maps?q=' . $lat . ',' . $lng . '&z=17&output=embed')),
|
||||
];
|
||||
}
|
||||
|
||||
public function closeAttachmentPreview(): void
|
||||
{
|
||||
$this->attachmentPreview = null;
|
||||
$this->attachmentMapPreview = null;
|
||||
}
|
||||
|
||||
public function closeAttachmentMapPreview(): void
|
||||
{
|
||||
$this->attachmentMapPreview = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public function getAttachmentDetails(TicketAttachment $attachment): array
|
||||
{
|
||||
return $this->parseAttachmentDescription((string) ($attachment->description ?? ''));
|
||||
}
|
||||
|
||||
public function getSelectedTicketProperty(): ?Ticket
|
||||
|
|
@ -1276,6 +1326,95 @@ private function syncTicketAttachmentsArchive(Ticket $ticket): void
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
private function parseAttachmentDescription(string $description): array
|
||||
{
|
||||
$parts = preg_split('/\s*\|\s*/', trim($description)) ?: [];
|
||||
|
||||
$details = [
|
||||
'primary_description' => '',
|
||||
'gps' => null,
|
||||
'maps_url' => null,
|
||||
'maps_embed_url' => null,
|
||||
'exif_datetime' => null,
|
||||
'device' => null,
|
||||
'file_label' => null,
|
||||
'original_label' => null,
|
||||
'other' => [],
|
||||
'full_description' => trim($description),
|
||||
];
|
||||
|
||||
foreach ($parts as $part) {
|
||||
$part = trim((string) $part);
|
||||
if ($part === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_starts_with($part, 'GPS:')) {
|
||||
$gpsValue = trim((string) Str::after($part, 'GPS:'));
|
||||
if (preg_match('/^\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*$/', $gpsValue, $matches) === 1) {
|
||||
$lat = (float) $matches[1];
|
||||
$lng = (float) $matches[2];
|
||||
|
||||
$details['gps'] = [
|
||||
'lat' => $lat,
|
||||
'lng' => $lng,
|
||||
'label' => number_format($lat, 5, '.', '') . ', ' . number_format($lng, 5, '.', ''),
|
||||
];
|
||||
$details['maps_embed_url'] = 'https://maps.google.com/maps?q=' . $lat . ',' . $lng . '&z=17&output=embed';
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_starts_with($part, 'Maps:')) {
|
||||
$details['maps_url'] = trim((string) Str::after($part, 'Maps:'));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_starts_with($part, 'EXIF:')) {
|
||||
$details['exif_datetime'] = trim((string) Str::after($part, 'EXIF:'));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_starts_with($part, 'Device:')) {
|
||||
$details['device'] = trim((string) Str::after($part, 'Device:'));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_starts_with($part, 'File:')) {
|
||||
$details['file_label'] = trim((string) Str::after($part, 'File:'));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str_starts_with($part, 'Orig:')) {
|
||||
$details['original_label'] = trim((string) Str::after($part, 'Orig:'));
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($details['primary_description'] === '') {
|
||||
$details['primary_description'] = $part;
|
||||
continue;
|
||||
}
|
||||
|
||||
$details['other'][] = $part;
|
||||
}
|
||||
|
||||
if ($details['maps_url'] === null && is_array($details['gps'])) {
|
||||
$details['maps_url'] = 'https://maps.google.com/?q=' . $details['gps']['lat'] . ',' . $details['gps']['lng'];
|
||||
}
|
||||
|
||||
if ($details['primary_description'] === '') {
|
||||
$details['primary_description'] = $details['full_description'] !== ''
|
||||
? $details['full_description']
|
||||
: 'Allegato ticket';
|
||||
}
|
||||
|
||||
return $details;
|
||||
}
|
||||
|
||||
private function archiveTicketAttachmentDocument(Ticket $ticket, TicketAttachment $attachment): void
|
||||
{
|
||||
if (! Schema::hasTable('documenti')) {
|
||||
|
|
|
|||
|
|
@ -673,15 +673,21 @@ private function loadOpenTickets(): void
|
|||
return;
|
||||
}
|
||||
|
||||
$stabileId = StabileContext::resolveActiveStabileId($user);
|
||||
if (! $stabileId) {
|
||||
$stabileIds = StabileContext::accessibleStabili($user)
|
||||
->pluck('id')
|
||||
->map(fn($value) => (int) $value)
|
||||
->filter(fn(int $value) => $value > 0)
|
||||
->values()
|
||||
->all();
|
||||
|
||||
if ($stabileIds === []) {
|
||||
$this->openTickets = [];
|
||||
return;
|
||||
}
|
||||
|
||||
$items = Ticket::query()
|
||||
->with('stabile:id,denominazione')
|
||||
->where('stabile_id', $stabileId)
|
||||
->whereIn('stabile_id', $stabileIds)
|
||||
->aperti()
|
||||
->orderByDesc('created_at')
|
||||
->limit(8)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
use App\Filament\Pages\Gescon\RubricaUniversaleArchivio;
|
||||
use App\Filament\Pages\Supporto\SupportoDashboard;
|
||||
use App\Filament\Pages\Supporto\TicketMobile;
|
||||
use App\Filament\Widgets\GoogleScadenziarioOverview;
|
||||
use App\Filament\Widgets\TicketMobileOverview;
|
||||
use App\Models\Fornitore;
|
||||
use App\Models\FornitoreDipendente;
|
||||
|
|
@ -116,7 +115,6 @@ public function panel(Panel $panel): Panel
|
|||
->discoverWidgets(in: app_path('Filament/Widgets'), for : 'App\\Filament\\Widgets')
|
||||
->widgets([
|
||||
AccountWidget::class,
|
||||
GoogleScadenziarioOverview::class,
|
||||
TicketMobileOverview::class,
|
||||
])
|
||||
->middleware([
|
||||
|
|
|
|||
|
|
@ -1,3 +1,62 @@
|
|||
# SESSION HANDOFF - 2026-04-08
|
||||
|
||||
## Contesto operativo attuale
|
||||
|
||||
- Stabile pilota: `0021`
|
||||
- Obiettivo reale: import sicuro solo per stabili gia presenti, senza fallback vuoti o inventati su unita, nominativi, millesimi e voci di spesa.
|
||||
- Regola utente vincolante:
|
||||
- l'ultimo anno disponibile e la base autorevole dello stato corrente
|
||||
- la chiave certa dell'unita e `condomin.cod_cond`
|
||||
- il codice mnemonico operatore resta `Palazzina/Scala/Interno`, ma non deve diventare la chiave interna in fallback
|
||||
- gli anni precedenti servono per costruire cronologia e variazioni, non per sovrascrivere il nominativo corrente
|
||||
- niente righe vuote, `interno` inventati, o completamenti artificiali che rompano telefono, nominativi o allineamento unita
|
||||
|
||||
## Fix gia applicate e verificate
|
||||
|
||||
- `app/Console/Commands/ImportGesconFullPipeline.php`
|
||||
- i placeholder `dettaglio_millesimi` a zero vengono creati solo con opzione esplicita `--fill-missing-millesimi`
|
||||
- migliorato il fallback di aggancio `voci_spesa -> tabelle_millesimali` quando manca il match per anno gestione
|
||||
- preflight staging reso sensibile a `cod_stabile + legacy_year`, non solo a `cod_stabile`
|
||||
- `app/Console/Commands/NetgesconQaMillesimiSpeseCommand.php`
|
||||
- nuovo comando QA per verificare allineamento unita, nominativi, millesimi, voci e archivi legacy
|
||||
- `app/Filament/Pages/Strumenti/PostItGestione.php`
|
||||
- raggruppamento post-it ora phone-first
|
||||
- chiamate ai gruppi `601` e `603` non risposte da un interno reale trattate come missed in UI
|
||||
- `app/Console/Commands/SmdrListenCommand.php`
|
||||
- nuove chiamate SMDR su `601`/`603` marcate missed se non emerge un vero interno finale
|
||||
|
||||
## Evidenze gia verificate
|
||||
|
||||
- Per `0021`, il preflight prima caricava solo `legacy_year=0003`; dopo il fix ha caricato correttamente anche `0004`.
|
||||
- Dry-run verificato:
|
||||
- `condomin 0021/0004 = 214`
|
||||
- `comproprietari 0021/0004 = 11`
|
||||
- `voc_spe 0021/0004 = 73`
|
||||
- Dopo il fix sulle voci, per `0021/0003` i codici legacy voce trovano tutti la corrispondente voce di dominio e non restano piu voci agganciate senza tabella default per i casi verificati.
|
||||
- Sul call board, il numero `3388155553` collassa in un solo gruppo; esiste almeno un caso reale `601 -> 206` che resta correttamente answered.
|
||||
|
||||
## Punto aperto principale
|
||||
|
||||
La prossima modifica strutturale da fare e in `ImportGesconFullPipeline.php`, nella parte che ricostruisce unita/nominativi da staging:
|
||||
|
||||
1. partire dall'ultimo anno disponibile come snapshot corrente autorevole
|
||||
2. identificare l'unita da `cod_cond` e usare `scala`, `interno`, `sub` solo come supporto di riconciliazione
|
||||
3. evitare la creazione di unita artificiali o fallback tipo `A/0` se non esiste una base certa in archivio
|
||||
4. aggiungere gli anni precedenti come variazioni storiche in `unita_immobiliare_nominativi`, senza riscrivere il corrente con dati piu vecchi
|
||||
5. mantenere continuita contabile con collegamento a unita e ruolo storico `C`/`I` per rate emesse e incassi
|
||||
|
||||
## Anomalie note da non coprire con fallback
|
||||
|
||||
- In dominio esistono ancora 5 unita attive di `0021` senza nominativo corrente: `A/0`, `A/42`, `A/43`, `A/215`, `A/216`.
|
||||
- Verifica fatta: queste unita non risultano in `gescon_import.condomin` per `0021/0003` nei dati controllati; non vanno auto-riempite con nominativi fittizi.
|
||||
- Decisione da prendere nella prossima sessione: review/archiviazione/disattivazione guidata, non compensazione automatica.
|
||||
|
||||
## Prompt di ripartenza consigliato
|
||||
|
||||
"Riparti da docs/ai/SESSION_HANDOFF.md, docs/ai/TOPICS_INDEX.md, docs/ai/LATEST_2_CHATS_RECOVERY.md e dal topic docs/ai/topics/import-unita-cod-cond.md. Prima allinea la logica di import unita/nominativi all'ultimo anno autorevole basato su cod_cond, senza fallback inventati. Poi valida l'impatto contabile su rate e incassi."
|
||||
|
||||
---
|
||||
|
||||
# SESSION HANDOFF – 2026-02-27
|
||||
|
||||
## Contesto operativo
|
||||
|
|
@ -50,10 +109,12 @@ ## Prompt di ripartenza consigliato
|
|||
## CHECKPOINT 2026-03-02 17:45:02
|
||||
|
||||
### Stato rapido
|
||||
|
||||
- Branch: main
|
||||
- Ultimo commit: 49002765 2025-11-10 Auto-sync commit: 2025-11-10 12:40:03
|
||||
|
||||
### File modificati (git status --short)
|
||||
|
||||
```
|
||||
M .gitignore
|
||||
M .vscode/tasks.json
|
||||
|
|
@ -10568,6 +10629,7 @@ ### File modificati (git status --short)
|
|||
```
|
||||
|
||||
### Ultimi file toccati (max 20)
|
||||
|
||||
```
|
||||
.gitignore
|
||||
.vscode/tasks.json
|
||||
|
|
@ -10592,15 +10654,19 @@ ### Ultimi file toccati (max 20)
|
|||
```
|
||||
|
||||
### Bug trovati / verificati in questa sessione
|
||||
-
|
||||
|
||||
-
|
||||
|
||||
### Decisioni prese
|
||||
-
|
||||
|
||||
-
|
||||
|
||||
### Prossimo step operativo
|
||||
-
|
||||
|
||||
-
|
||||
|
||||
### Prompt di ripartenza
|
||||
|
||||
Riparti da docs/ai/SESSION_HANDOFF.md, usa l'ultimo CHECKPOINT, continua dal "Prossimo step operativo" senza rifare analisi generale.
|
||||
|
||||
---
|
||||
|
|
@ -10608,10 +10674,12 @@ ### Prompt di ripartenza
|
|||
## CHECKPOINT 2026-03-02 18:33:07
|
||||
|
||||
### Stato rapido
|
||||
|
||||
- Branch: main
|
||||
- Ultimo commit: 49002765 2025-11-10 Auto-sync commit: 2025-11-10 12:40:03
|
||||
|
||||
### File modificati (git status --short)
|
||||
|
||||
```
|
||||
M .gitignore
|
||||
M .vscode/tasks.json
|
||||
|
|
@ -21126,6 +21194,7 @@ ### File modificati (git status --short)
|
|||
```
|
||||
|
||||
### Ultimi file toccati (max 20)
|
||||
|
||||
```
|
||||
.gitignore
|
||||
.vscode/tasks.json
|
||||
|
|
@ -21150,15 +21219,19 @@ ### Ultimi file toccati (max 20)
|
|||
```
|
||||
|
||||
### Bug trovati / verificati in questa sessione
|
||||
-
|
||||
|
||||
-
|
||||
|
||||
### Decisioni prese
|
||||
-
|
||||
|
||||
-
|
||||
|
||||
### Prossimo step operativo
|
||||
-
|
||||
|
||||
-
|
||||
|
||||
### Prompt di ripartenza
|
||||
|
||||
Riparti da docs/ai/SESSION_HANDOFF.md, usa l'ultimo CHECKPOINT, continua dal "Prossimo step operativo" senza rifare analisi generale.
|
||||
|
||||
---
|
||||
|
|
@ -21166,10 +21239,12 @@ ### Prompt di ripartenza
|
|||
## CHECKPOINT 2026-03-03 09:35:25
|
||||
|
||||
### Stato rapido
|
||||
|
||||
- Branch: main
|
||||
- Ultimo commit: 49002765 2025-11-10 Auto-sync commit: 2025-11-10 12:40:03
|
||||
|
||||
### File modificati (git status --short)
|
||||
|
||||
```
|
||||
M .gitignore
|
||||
M .vscode/tasks.json
|
||||
|
|
@ -31684,6 +31759,7 @@ ### File modificati (git status --short)
|
|||
```
|
||||
|
||||
### Ultimi file toccati (max 20)
|
||||
|
||||
```
|
||||
.gitignore
|
||||
.vscode/tasks.json
|
||||
|
|
@ -31708,15 +31784,19 @@ ### Ultimi file toccati (max 20)
|
|||
```
|
||||
|
||||
### Bug trovati / verificati in questa sessione
|
||||
-
|
||||
|
||||
-
|
||||
|
||||
### Decisioni prese
|
||||
-
|
||||
|
||||
-
|
||||
|
||||
### Prossimo step operativo
|
||||
-
|
||||
|
||||
-
|
||||
|
||||
### Prompt di ripartenza
|
||||
|
||||
Riparti da docs/ai/SESSION_HANDOFF.md, usa l'ultimo CHECKPOINT, continua dal "Prossimo step operativo" senza rifare analisi generale.
|
||||
|
||||
---
|
||||
|
|
@ -31724,10 +31804,12 @@ ### Prompt di ripartenza
|
|||
## CHECKPOINT 2026-03-03 10:26:27
|
||||
|
||||
### Stato rapido
|
||||
|
||||
- Branch: main
|
||||
- Ultimo commit: 49002765 2025-11-10 Auto-sync commit: 2025-11-10 12:40:03
|
||||
|
||||
### File modificati (git status --short)
|
||||
|
||||
```
|
||||
M .gitignore
|
||||
M .vscode/tasks.json
|
||||
|
|
@ -42242,6 +42324,7 @@ ### File modificati (git status --short)
|
|||
```
|
||||
|
||||
### Ultimi file toccati (max 20)
|
||||
|
||||
```
|
||||
.gitignore
|
||||
.vscode/tasks.json
|
||||
|
|
@ -42266,13 +42349,17 @@ ### Ultimi file toccati (max 20)
|
|||
```
|
||||
|
||||
### Bug trovati / verificati in questa sessione
|
||||
-
|
||||
|
||||
-
|
||||
|
||||
### Decisioni prese
|
||||
-
|
||||
|
||||
-
|
||||
|
||||
### Prossimo step operativo
|
||||
-
|
||||
|
||||
-
|
||||
|
||||
### Prompt di ripartenza
|
||||
|
||||
Riparti da docs/ai/SESSION_HANDOFF.md, usa l'ultimo CHECKPOINT, continua dal "Prossimo step operativo" senza rifare analisi generale.
|
||||
|
|
|
|||
|
|
@ -35,4 +35,18 @@ .netgescon-footer {
|
|||
width: 100% !important;
|
||||
flex: 0 0 auto;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.fi-sidebar-nav,
|
||||
.fi-sidebar-nav-groups {
|
||||
row-gap: calc(var(--spacing) * 4);
|
||||
}
|
||||
|
||||
.fi-sidebar-group-btn,
|
||||
.fi-sidebar-group-dropdown-trigger-btn {
|
||||
padding-block: calc(var(--spacing) * 1.25);
|
||||
}
|
||||
|
||||
.fi-sidebar-group-label {
|
||||
line-height: calc(var(--spacing) * 5);
|
||||
}
|
||||
|
|
@ -1,17 +1,22 @@
|
|||
@php
|
||||
$user = \Illuminate\Support\Facades\Auth::user();
|
||||
$stabileId = null;
|
||||
$stabileIds = [];
|
||||
|
||||
if ($user instanceof \App\Models\User) {
|
||||
$stabileId = \App\Support\StabileContext::resolveActiveStabileId($user);
|
||||
$stabileIds = \App\Support\StabileContext::accessibleStabili($user)
|
||||
->pluck('id')
|
||||
->map(fn($value) => (int) $value)
|
||||
->filter(fn(int $value) => $value > 0)
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
$avvisiCount = 0;
|
||||
$urgenzeCount = 0;
|
||||
|
||||
if ($stabileId) {
|
||||
if ($stabileIds !== []) {
|
||||
$baseQuery = \App\Models\Ticket::query()
|
||||
->where('stabile_id', $stabileId)
|
||||
->whereIn('stabile_id', $stabileIds)
|
||||
->whereIn('stato', ['Aperto', 'Preso in Carico', 'In Lavorazione']);
|
||||
|
||||
$urgenzeCount = (clone $baseQuery)->where('priorita', 'Urgente')->count();
|
||||
|
|
|
|||
|
|
@ -5,11 +5,17 @@
|
|||
$avvisi = 0;
|
||||
|
||||
if ($user instanceof \App\Models\User) {
|
||||
$stabileId = \App\Support\StabileContext::resolveActiveStabileId($user);
|
||||
if ($stabileId) {
|
||||
$stabileIds = \App\Support\StabileContext::accessibleStabili($user)
|
||||
->pluck('id')
|
||||
->map(fn($value) => (int) $value)
|
||||
->filter(fn(int $value) => $value > 0)
|
||||
->values()
|
||||
->all();
|
||||
|
||||
if ($stabileIds !== []) {
|
||||
$base = \App\Models\Ticket::query()
|
||||
->where('stabile_id', $stabileId)
|
||||
->where('stato', 'Aperto');
|
||||
->whereIn('stabile_id', $stabileIds)
|
||||
->whereIn('stato', ['Aperto', 'Preso in Carico', 'In Lavorazione']);
|
||||
|
||||
$urgenti = (clone $base)->where('priorita', 'Urgente')->count();
|
||||
$avvisi = (clone $base)->where('priorita', '!=', 'Urgente')->count();
|
||||
|
|
|
|||
|
|
@ -7,35 +7,42 @@
|
|||
Se importi `unità`, `soggetti` o `diritti`, la pagina ora lancia anche il re-sync di anagrafiche, ruoli rubrica e bonifica recapiti/contatti collegati allo stabile selezionato.
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-slate-200 bg-white p-4">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<div class="text-sm font-semibold text-slate-900">Azioni operative</div>
|
||||
<div class="mt-1 text-xs text-slate-600">Prima esegui i pulsanti di preparazione, poi il flusso di import, infine gli eventuali riallineamenti.</div>
|
||||
</div>
|
||||
<div class="text-xs text-slate-600">Stabile attuale: <span class="font-semibold text-slate-900">{{ $selectedLabel !== '' ? $selectedLabel : 'nessuno' }}</span></div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 flex flex-wrap gap-2">
|
||||
<x-filament::button type="button" size="sm" wire:click="loadStabili">Carica elenco stabili</x-filament::button>
|
||||
<x-filament::button type="button" size="sm" color="gray" wire:click="caricaAnniDaGenerale" :disabled="empty($stabiliOptions) || blank(data_get($data ?? [], 'stabile_code'))">Carica anni</x-filament::button>
|
||||
<x-filament::button type="button" size="sm" color="success" wire:click="creaStabileDaMdb" :disabled="empty($stabiliOptions) || blank(data_get($data ?? [], 'stabile_code'))">Crea / apri stabile</x-filament::button>
|
||||
<x-filament::button type="button" size="sm" color="gray" wire:click="eseguiImportSelezionato" :disabled="empty($stabiliOptions) || blank(data_get($data ?? [], 'stabile_code'))">Importa dati selezionati</x-filament::button>
|
||||
<x-filament::button type="button" size="sm" color="primary" wire:click="eseguiImportAllineamentoCompleto" :disabled="empty($stabiliOptions) || blank(data_get($data ?? [], 'stabile_code'))">Import + allineamento</x-filament::button>
|
||||
<x-filament::button type="button" size="sm" color="info" wire:click="aggiornaFornitoriGescon" :disabled="blank(data_get($data ?? [], 'path_root'))">Aggiorna fornitori</x-filament::button>
|
||||
<x-filament::button type="button" size="sm" color="warning" wire:click="risincronizzaRelazioniImportate" :disabled="empty($stabiliOptions) || blank(data_get($data ?? [], 'stabile_code'))">Risincronizza relazioni</x-filament::button>
|
||||
<x-filament::button type="button" size="sm" color="info" wire:click="aggiornaAnagraficheDaCondomin" :disabled="empty($stabiliOptions) || blank(data_get($data ?? [], 'stabile_code'))">Sync anagrafiche</x-filament::button>
|
||||
<x-filament::button type="button" size="sm" color="warning" wire:click="refreshImportTagLegacyFornitoriGlobale" :disabled="blank(data_get($data ?? [], 'path_root'))">Refresh TAG fornitori</x-filament::button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 xl:grid-cols-3">
|
||||
<div class="rounded-xl border border-slate-200 bg-white p-4">
|
||||
<div class="text-sm font-semibold text-slate-900">1. Setup archivio</div>
|
||||
<div class="mt-3 flex flex-wrap gap-2">
|
||||
<x-filament::button type="button" size="sm" wire:click="loadStabili">Carica elenco stabili</x-filament::button>
|
||||
<x-filament::button type="button" size="sm" color="gray" wire:click="caricaAnniDaGenerale" :disabled="empty($stabiliOptions) || blank(data_get($data ?? [], 'stabile_code'))">Carica anni</x-filament::button>
|
||||
<x-filament::button type="button" size="sm" color="success" wire:click="creaStabileDaMdb" :disabled="empty($stabiliOptions) || blank(data_get($data ?? [], 'stabile_code'))">Crea / apri stabile</x-filament::button>
|
||||
</div>
|
||||
<div class="mt-3 text-xs text-slate-600">Stabile attuale: {{ $selectedLabel !== '' ? $selectedLabel : 'nessuno' }}</div>
|
||||
<div class="mt-2 text-xs text-slate-600">Carica elenco stabili, recupera gli anni dalla base generale e crea o riapri lo stabile locale prima dell'import vero e proprio.</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-slate-200 bg-white p-4">
|
||||
<div class="text-sm font-semibold text-slate-900">2. Import / aggiornamento</div>
|
||||
<div class="mt-3 flex flex-wrap gap-2">
|
||||
<x-filament::button type="button" size="sm" color="gray" wire:click="eseguiImportSelezionato" :disabled="empty($stabiliOptions) || blank(data_get($data ?? [], 'stabile_code'))">Importa dati selezionati</x-filament::button>
|
||||
<x-filament::button type="button" size="sm" color="primary" wire:click="eseguiImportAllineamentoCompleto" :disabled="empty($stabiliOptions) || blank(data_get($data ?? [], 'stabile_code'))">Import + allineamento</x-filament::button>
|
||||
<x-filament::button type="button" size="sm" color="info" wire:click="aggiornaFornitoriGescon" :disabled="blank(data_get($data ?? [], 'path_root'))">Aggiorna fornitori</x-filament::button>
|
||||
</div>
|
||||
<div class="mt-3 text-xs text-slate-600">Usa `Import + allineamento` come flusso principale; `Importa dati selezionati` serve per slice mirate.</div>
|
||||
<div class="mt-2 text-xs text-slate-600">Usa Import + allineamento come flusso principale. Importa dati selezionati resta utile solo per slice mirate o recuperi puntuali.</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-slate-200 bg-white p-4">
|
||||
<div class="text-sm font-semibold text-slate-900">3. Re-sync relazioni</div>
|
||||
<div class="mt-3 flex flex-wrap gap-2">
|
||||
<x-filament::button type="button" size="sm" color="warning" wire:click="risincronizzaRelazioniImportate" :disabled="empty($stabiliOptions) || blank(data_get($data ?? [], 'stabile_code'))">Risincronizza relazioni</x-filament::button>
|
||||
<x-filament::button type="button" size="sm" color="info" wire:click="aggiornaAnagraficheDaCondomin" :disabled="empty($stabiliOptions) || blank(data_get($data ?? [], 'stabile_code'))">Sync anagrafiche</x-filament::button>
|
||||
<x-filament::button type="button" size="sm" color="warning" wire:click="refreshImportTagLegacyFornitoriGlobale" :disabled="blank(data_get($data ?? [], 'path_root'))">Refresh TAG fornitori</x-filament::button>
|
||||
</div>
|
||||
<div class="mt-3 text-xs text-slate-600">Serve quando i dati locali esistono già e vuoi riallineare rubrica, ruoli e recapiti senza reimportare tutto.</div>
|
||||
<div class="mt-2 text-xs text-slate-600">Serve quando i dati locali esistono già e vuoi riallineare rubrica, ruoli, recapiti e tag fornitori senza rilanciare l'intero import.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -82,12 +82,26 @@
|
|||
<div class="mt-3 space-y-1 text-[11px] text-slate-600">
|
||||
<div>Branch locale: <span class="font-mono font-medium">{{ $this->gitCurrentBranch ?? '-' }}</span></div>
|
||||
<div>Commit locale: <span class="font-mono font-medium">{{ $this->gitCurrentCommit ?? '-' }}</span></div>
|
||||
<div>Data commit locale: <span class="font-medium">{{ $this->gitCurrentCommitDate ?? '-' }}</span></div>
|
||||
<div>Commit remoto: <span class="font-mono font-medium">{{ $this->gitRemoteCommit ?? '-' }}</span></div>
|
||||
<div>Data commit remoto: <span class="font-medium">{{ $this->gitRemoteCommitDate ?? '-' }}</span></div>
|
||||
<div>Tracking: <span class="font-medium">{{ $this->gitAheadBehind }}</span></div>
|
||||
<div>Aggiornamenti da caricare sul nodo: <span class="font-semibold {{ (int) $this->pendingGitCommitCount > 0 ? 'text-amber-700' : 'text-emerald-700' }}">{{ (int) $this->pendingGitCommitCount }}</span></div>
|
||||
<div>Commit locali da inviare: <span class="font-semibold {{ (int) $this->gitAheadCount > 0 ? 'text-sky-700' : 'text-slate-700' }}">{{ (int) $this->gitAheadCount }}</span></div>
|
||||
<div>Working tree: <span class="font-medium {{ $this->gitWorkingTreeDirty ? 'text-rose-700' : 'text-emerald-700' }}">{{ $this->gitWorkingTreeDirty ? 'sporco' : 'pulito' }}</span></div>
|
||||
<div>Ultima sync Git: <span class="font-medium">{{ $this->lastGitSyncAt ?? '-' }}</span></div>
|
||||
<div>Ultimo update: <span class="font-medium">{{ $this->lastUpdateAt ?? '-' }}</span></div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 rounded-lg border border-slate-200 bg-slate-50 p-3 text-[11px] text-slate-600">
|
||||
Git sync e update codice aggiornano i file versionati nel repository. Gli archivi caricati manualmente dentro storage o cartelle private non vengono trasferiti da Git, salvo procedure dedicate di backup/sync dati.
|
||||
</div>
|
||||
|
||||
@if(filled($this->pendingGitPreviewIssue))
|
||||
<div class="mt-3 rounded-lg border border-amber-300 bg-amber-50 p-3 text-[11px] text-amber-900">
|
||||
{{ $this->pendingGitPreviewIssue }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,5 @@
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@livewire(\App\Filament\Widgets\GoogleScadenziarioOverview::class)
|
||||
</div>
|
||||
</x-filament-panels::page>
|
||||
|
|
|
|||
|
|
@ -332,6 +332,7 @@
|
|||
|
||||
<div class="mt-4 grid gap-3 sm:grid-cols-2">
|
||||
@forelse($ticket->attachments as $a)
|
||||
@php($details = $this->getAttachmentDetails($a))
|
||||
<div class="rounded-xl border bg-white p-3 text-xs shadow-sm">
|
||||
<div class="aspect-[4/3] rounded-lg border bg-gray-50 p-2">
|
||||
@if($a->isImage())
|
||||
|
|
@ -347,10 +348,48 @@
|
|||
</div>
|
||||
|
||||
<div class="mt-2 font-medium">{{ $a->original_file_name }}</div>
|
||||
<div class="mt-1 text-gray-600">{{ $a->description ?: 'Nessuna descrizione' }}</div>
|
||||
<div class="mt-1 text-gray-700">{{ $details['primary_description'] ?: 'Nessuna descrizione' }}</div>
|
||||
<div class="mt-2 flex flex-wrap gap-1">
|
||||
@if(!empty($details['gps']))
|
||||
<span class="inline-flex items-center rounded-full bg-emerald-100 px-2 py-0.5 text-[10px] font-semibold text-emerald-800">GPS</span>
|
||||
@endif
|
||||
@if(!empty($details['exif_datetime']))
|
||||
<span class="inline-flex items-center rounded-full bg-sky-100 px-2 py-0.5 text-[10px] font-semibold text-sky-800">EXIF</span>
|
||||
@endif
|
||||
@if(!empty($details['device']))
|
||||
<span class="inline-flex items-center rounded-full bg-slate-100 px-2 py-0.5 text-[10px] font-semibold text-slate-700">{{ $details['device'] }}</span>
|
||||
@endif
|
||||
</div>
|
||||
@if(!empty($details['gps']) || !empty($details['exif_datetime']) || !empty($details['file_label']) || !empty($details['original_label']) || !empty($details['other']))
|
||||
<div class="mt-2 rounded-lg border border-slate-200 bg-slate-50 p-2 text-[11px] text-slate-700">
|
||||
@if(!empty($details['gps']))
|
||||
<div><span class="font-medium">Coordinate:</span> {{ $details['gps']['label'] }}</div>
|
||||
@endif
|
||||
@if(!empty($details['exif_datetime']))
|
||||
<div><span class="font-medium">Data EXIF:</span> {{ $details['exif_datetime'] }}</div>
|
||||
@endif
|
||||
@if(!empty($details['device']))
|
||||
<div><span class="font-medium">Dispositivo:</span> {{ $details['device'] }}</div>
|
||||
@endif
|
||||
@if(!empty($details['file_label']))
|
||||
<div><span class="font-medium">Nome file:</span> {{ $details['file_label'] }}</div>
|
||||
@endif
|
||||
@if(!empty($details['original_label']))
|
||||
<div><span class="font-medium">Origine:</span> {{ $details['original_label'] }}</div>
|
||||
@endif
|
||||
@foreach($details['other'] as $otherPart)
|
||||
<div>{{ $otherPart }}</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
<div class="mt-2 text-[11px] text-gray-500">{{ optional($a->created_at)->format('d/m/Y H:i') }}</div>
|
||||
|
||||
<button type="button" wire:click="openAttachmentPreview({{ (int) $a->id }})" class="mt-2 inline-flex items-center rounded-md bg-indigo-600 px-2 py-1 text-[11px] font-medium text-white hover:bg-indigo-500">Visualizza in modal</button>
|
||||
<div class="mt-2 flex flex-wrap gap-2">
|
||||
<button type="button" wire:click="openAttachmentPreview({{ (int) $a->id }})" class="inline-flex items-center rounded-md bg-indigo-600 px-2 py-1 text-[11px] font-medium text-white hover:bg-indigo-500">Visualizza allegato</button>
|
||||
@if(!empty($details['gps']))
|
||||
<button type="button" wire:click="openAttachmentMapPreview({{ (int) $a->id }})" class="inline-flex items-center rounded-md bg-emerald-600 px-2 py-1 text-[11px] font-medium text-white hover:bg-emerald-500">Apri mappa</button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="text-xs text-gray-500">Nessun allegato.</div>
|
||||
|
|
@ -545,8 +584,21 @@
|
|||
<div class="flex items-center justify-between border-b px-4 py-3">
|
||||
<div>
|
||||
<div class="text-sm font-semibold">{{ $attachmentPreview['name'] ?? 'Allegato' }}</div>
|
||||
@if(!empty($attachmentPreview['description']))
|
||||
<div class="text-xs text-gray-500">{{ $attachmentPreview['description'] }}</div>
|
||||
@if(!empty($attachmentPreview['details']['primary_description']))
|
||||
<div class="text-xs text-gray-500">{{ $attachmentPreview['details']['primary_description'] }}</div>
|
||||
@endif
|
||||
@if(!empty($attachmentPreview['details']['gps']) || !empty($attachmentPreview['details']['exif_datetime']) || !empty($attachmentPreview['details']['device']))
|
||||
<div class="mt-1 flex flex-wrap gap-2 text-[11px] text-slate-600">
|
||||
@if(!empty($attachmentPreview['details']['gps']))
|
||||
<span>GPS {{ $attachmentPreview['details']['gps']['label'] }}</span>
|
||||
@endif
|
||||
@if(!empty($attachmentPreview['details']['exif_datetime']))
|
||||
<span>EXIF {{ $attachmentPreview['details']['exif_datetime'] }}</span>
|
||||
@endif
|
||||
@if(!empty($attachmentPreview['details']['device']))
|
||||
<span>{{ $attachmentPreview['details']['device'] }}</span>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
|
|
@ -560,12 +612,21 @@
|
|||
<button type="button" x-on:click="nextPage()" class="rounded-md bg-slate-100 px-2 py-1 text-xs hover:bg-slate-200">Pagina successiva</button>
|
||||
<button type="button" x-on:click="printPdf()" class="rounded-md bg-slate-100 px-2 py-1 text-xs hover:bg-slate-200">Stampa PDF</button>
|
||||
@endif
|
||||
@if(!empty($attachmentPreview['details']['gps']))
|
||||
<button type="button" wire:click="openAttachmentMapPreview({{ (int) ($attachmentPreview['id'] ?? 0) }})" class="rounded-md bg-emerald-100 px-2 py-1 text-xs text-emerald-800 hover:bg-emerald-200">Google Maps</button>
|
||||
@endif
|
||||
<a href="{{ $attachmentPreview['url'] }}" target="_blank" class="rounded-md bg-slate-100 px-2 py-1 text-xs hover:bg-slate-200">Apri in nuova scheda</a>
|
||||
<button type="button" wire:click="closeAttachmentPreview" class="rounded-md bg-gray-100 px-2 py-1 text-xs hover:bg-gray-200">Chiudi</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-[calc(92vh-64px)] overflow-auto p-4">
|
||||
@if(!empty($attachmentPreview['details']['full_description']))
|
||||
<div class="mb-3 rounded-lg border border-slate-200 bg-slate-50 p-3 text-xs text-slate-700">
|
||||
<div class="font-medium text-slate-900">Dettagli allegato</div>
|
||||
<div class="mt-1 whitespace-pre-wrap">{{ $attachmentPreview['details']['full_description'] }}</div>
|
||||
</div>
|
||||
@endif
|
||||
@if($attachmentPreview['is_image'] ?? false)
|
||||
<div class="h-full overflow-auto rounded-lg bg-slate-50 p-4">
|
||||
<div class="flex min-h-full min-w-full items-start justify-center">
|
||||
|
|
@ -584,4 +645,24 @@
|
|||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($attachmentMapPreview)
|
||||
<div class="fixed inset-0 z-[60] flex items-center justify-center bg-black/70 p-4">
|
||||
<div class="h-[88vh] w-full max-w-5xl rounded-xl bg-white shadow-2xl">
|
||||
<div class="flex items-center justify-between border-b px-4 py-3">
|
||||
<div>
|
||||
<div class="text-sm font-semibold">Mappa allegato</div>
|
||||
<div class="text-xs text-slate-500">{{ $attachmentMapPreview['name'] ?? 'Allegato' }} · {{ $attachmentMapPreview['label'] ?? '' }}</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ $attachmentMapPreview['maps_url'] }}" target="_blank" rel="noopener noreferrer" class="rounded-md bg-emerald-100 px-2 py-1 text-xs text-emerald-800 hover:bg-emerald-200">Apri Google Maps</a>
|
||||
<button type="button" wire:click="closeAttachmentMapPreview" class="rounded-md bg-gray-100 px-2 py-1 text-xs hover:bg-gray-200">Chiudi</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-[calc(88vh-64px)] p-4">
|
||||
<iframe src="{{ $attachmentMapPreview['embed_url'] }}" class="h-full w-full rounded-lg border" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</x-filament-panels::page>
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@
|
|||
|
||||
<div x-show="localCameraPreviews.length || localAttachmentPreviews.length" x-cloak class="mt-3 rounded-xl border border-sky-200 bg-sky-50 p-3">
|
||||
<div class="text-xs font-semibold text-sky-900">Anteprima immediata sul telefono</div>
|
||||
<div class="mt-1 text-[11px] text-sky-800">Le foto e gli allegati selezionati si vedono subito qui, prima dell'accodamento definitivo nella bozza ticket.</div>
|
||||
<div class="mt-1 text-[11px] text-sky-800">Le foto e gli allegati selezionati si vedono subito qui mentre entrano in coda. Nella griglia sotto ogni miniatura resta nello stesso box della sua descrizione.</div>
|
||||
|
||||
<template x-if="localCameraPreviews.length">
|
||||
<div class="mt-3">
|
||||
|
|
@ -419,13 +419,13 @@
|
|||
@if(count($this->selectedUploads) > 0)
|
||||
<div class="md:col-span-2">
|
||||
<div class="mb-2 text-xs font-semibold text-gray-700">Coda allegati pronta per il ticket</div>
|
||||
<div class="mb-2 text-[11px] text-gray-500">Qui sotto restano solo i dati operativi del file. L'anteprima immagine resta nel riquadro sopra fino all'invio del ticket.</div>
|
||||
<div class="mb-2 text-[11px] text-gray-500">Ogni allegato mantiene qui miniatura, protocollo e descrizione nello stesso riquadro, cosi puoi descrivere correttamente piu foto consecutive.</div>
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
@foreach($this->selectedUploads as $upload)
|
||||
<div class="rounded-xl border bg-white p-3 text-xs shadow-sm">
|
||||
@if($upload['is_image'] && $upload['preview_url'])
|
||||
<div class="mb-2 aspect-[4/3] overflow-hidden rounded-lg border bg-slate-50">
|
||||
<img src="{{ $upload['preview_url'] }}" alt="{{ $upload['name'] }}" class="h-full w-full object-cover" />
|
||||
@if($upload['is_image'])
|
||||
<div class="mb-2 aspect-[4/3] overflow-hidden rounded-lg border bg-slate-50" x-show="Boolean(@js($upload['preview_url']) || previewFor(@js($upload['name']), @js($upload['original_name'])))">
|
||||
<img x-bind:src="@js($upload['preview_url']) || previewFor(@js($upload['name']), @js($upload['original_name']))" alt="{{ $upload['name'] }}" class="h-full w-full object-cover" />
|
||||
</div>
|
||||
@endif
|
||||
<div class="flex flex-wrap items-center gap-2 rounded-lg border bg-gray-50 px-3 py-2">
|
||||
|
|
@ -433,7 +433,7 @@
|
|||
{{ $upload['is_image'] ? 'Immagine' : (strtoupper(pathinfo($upload['name'], PATHINFO_EXTENSION) ?: 'FILE')) }}
|
||||
</span>
|
||||
<div class="text-[11px] text-gray-600">
|
||||
{{ $upload['is_image'] ? 'Anteprima visibile sopra' : 'Allegato pronto per l\'invio' }}
|
||||
{{ $upload['is_image'] ? 'Miniatura agganciata a questa descrizione' : 'Allegato pronto per l\'invio' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user