Refonte design V1

This commit is contained in:
mrKamoo
2026-10-02 13:06:14 +02:00
parent 429dd9de5e
commit 83e1ce00a9
20 changed files with 1403 additions and 531 deletions
@@ -1,79 +1,90 @@
@extends('layouts.app')
@section('title', 'Images — Signatures CABM')
@section('title', 'Images — Signatures')
@section('content')
@php
$tag = fn (string $inner) => '{'.'{'.$inner.'}'.'}';
$isAdmin = auth()->user()->hasRole('admin');
@endphp
<h1>Images</h1>
<p class="muted" style="margin-top:-8px">
Logos et bannières utilisés dans les signatures, publiés sur <code>{{ config('signature.public_url') }}/img/</code>.
Les destinataires les chargent depuis ce serveur : visez moins de 100 Ko et la largeur réelle d'affichage (ex. 120 px pour un logo, 600 px pour une bannière).
</p>
<header class="page-head">
<div>
<h1>Images</h1>
<p class="lead">
Logos et bannières des signatures, publiés sur <code>{{ config('signature.public_url') }}/img/</code>.
Les destinataires les téléchargent depuis ce serveur : visez moins de 100 Ko et la largeur réelle d'affichage
(120 px pour un logo, 600 px pour une bannière).
</p>
</div>
</header>
<div class="card">
<form method="post" action="{{ route('admin.images.store') }}" enctype="multipart/form-data" class="card" data-busy="Envoi…">
@csrf
<h2>Ajouter une image</h2>
<form method="post" action="{{ route('admin.images.store') }}" enctype="multipart/form-data" class="row" style="align-items:flex-end">
@csrf
<div style="flex:2;min-width:240px">
<label for="image">Fichier (PNG, JPEG, GIF — {{ (int) ($maxKb / 1024) }} Mo max.)</label>
<input type="file" id="image" name="image" accept="image/png,image/jpeg,image/gif" required>
<div class="field-row" style="align-items:flex-end">
<div style="flex:2">
<label class="dropzone" data-dropzone>
<input type="file" id="image" name="image" accept="image/png,image/jpeg,image/gif" required aria-label="Fichier image">
<x-icon name="cloud-upload" :size="26" />
<span data-prompt>Glissez une image ici ou <u>parcourez vos fichiers</u></span>
<span class="dropzone-name" data-name hidden></span>
<span class="muted">PNG, JPEG ou GIF, {{ (int) ($maxKb / 1024) }} Mo maximum</span>
</label>
</div>
<div style="flex:1;min-width:200px">
<label for="name">Nom (facultatif)</label>
<div>
<label for="name">Nom du fichier (facultatif)</label>
<input type="text" id="name" name="name" maxlength="80" placeholder="ex. logo-cabm">
<p class="hint">Sans nom, celui du fichier envoyé est repris.</p>
<button class="btn" type="submit" style="margin-top:12px"><x-icon name="cloud-upload" /><span data-label>Envoyer</span></button>
</div>
<button class="btn" type="submit">Envoyer</button>
</form>
</div>
</form>
<div class="card-head">
<h2>Images en ligne</h2>
<span class="badge plain">{{ $images->count() }}</span>
</div>
<div class="card">
<h2>Images en ligne ({{ $images->count() }})</h2>
<table class="list">
<thead><tr><th style="width:150px">Aperçu</th><th>Fichier</th><th>À coller dans un template</th><th>Utilisée par</th><th></th></tr></thead>
<tbody>
@forelse ($images as $img)
@if ($images->isEmpty())
<div class="card empty">
<x-icon name="images" :size="32" />
<p><strong>Aucune image</strong>Commencez par le logo : le template fourni par défaut attend un fichier nommé logo-cabm.png.</p>
</div>
@else
<div class="gallery">
@foreach ($images as $img)
@php $snippet = '<img src="'.$tag('site_url').'/img/'.$img['name'].'" alt="" width="'.($img['width'] ?? '').'" style="display:block;border:0">'; @endphp
<tr>
<td><img src="{{ asset('img/'.$img['name']) }}" alt="" style="max-width:140px;max-height:70px;border:1px solid var(--line);background:#fff"></td>
<td>
<a href="{{ $img['url'] }}" target="_blank" rel="noopener">{{ $img['name'] }}</a>
<div class="muted">{{ $img['width'] }}×{{ $img['height'] }} px · {{ $img['kb'] }} Ko
@if ($img['kb'] > 150) <span class="badge warn">lourde</span> @endif</div>
</td>
<td>
<div class="row">
<code style="font-size:12px;word-break:break-all">{{ $snippet }}</code>
<button type="button" class="btn secondary small" data-copy="{{ $snippet }}">Copier</button>
<article class="media">
<a class="media-thumb" href="{{ $img['url'] }}" target="_blank" rel="noopener" title="Ouvrir l'image publique">
<img src="{{ asset('img/'.$img['name']) }}" alt="" loading="lazy">
</a>
<div class="media-body">
<div class="media-name">{{ $img['name'] }}</div>
<div class="muted">
{{ $img['width'] }}×{{ $img['height'] }} px, {{ $img['kb'] }} Ko
@if ($img['kb'] > 150) <span class="badge warn">lourde</span> @endif
</div>
</td>
<td class="muted">{{ $img['used_in'] ? implode(', ', $img['used_in']) : '—' }}</td>
<td style="text-align:right">
<div class="muted" style="margin-top:6px">
{{ $img['used_in'] ? 'Utilisée par : '.implode(', ', $img['used_in']) : 'Non utilisée' }}
</div>
</div>
<div class="media-foot">
<button type="button" class="btn secondary small" data-copy="{{ $snippet }}" title="{{ $snippet }}">
<x-icon name="copy" :size="15" /><span data-label>Copier la balise</span>
</button>
<span class="spacer"></span>
@if ($isAdmin)
<form class="inline" method="post" action="{{ route('admin.images.destroy', $img['name']) }}"
onsubmit="return confirm('Supprimer {{ $img['name'] }} ? Les signatures déjà envoyées qui l\'affichent ne la montreront plus.')">
data-confirm="Supprimer {{ $img['name'] }} ? Les messages déjà envoyés qui l'affichent ne la montreront plus."
data-confirm-label="Supprimer" data-confirm-danger>
@csrf @method('DELETE')
<button class="btn danger small" type="submit" @disabled($img['used_in'])>Supprimer</button>
<button class="btn danger small icon-only" type="submit" @disabled($img['used_in'])
title="{{ $img['used_in'] ? 'Retirez-la d\'abord des templates et campagnes' : 'Supprimer' }}" aria-label="Supprimer {{ $img['name'] }}">
<x-icon name="trash-2" :size="15" />
</button>
</form>
@endif
</td>
</tr>
@empty
<tr><td colspan="5" class="muted">Aucune image. Commencez par le logo (ex. logo-cabm.png, utilisé par le template par défaut).</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
</article>
@endforeach
</div>
@endif
@endsection
@push('scripts')
<script>
document.addEventListener('click', async (e) => {
const text = e.target.dataset && e.target.dataset.copy;
if (!text) return;
try { await navigator.clipboard.writeText(text); e.target.textContent = 'Copié'; }
catch { e.target.textContent = 'Copie impossible'; }
setTimeout(() => { e.target.textContent = 'Copier'; }, 1500);
});
</script>
@endpush