backoffice first commit

This commit is contained in:
2026-07-17 11:34:15 +02:00
parent 761db06329
commit 1c40ef6601
159 changed files with 25830 additions and 7875 deletions
+9
View File
@@ -0,0 +1,9 @@
@import 'tailwindcss';
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
@source '../../storage/framework/views/*.php';
@theme {
--font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
'Segoe UI Symbol', 'Noto Color Emoji';
}
+1
View File
@@ -0,0 +1 @@
//
@@ -0,0 +1,23 @@
<x-mail::message>
<x-slot:header>
<x-mail::header :url="config('app.url')">
<img src="{{ config('app.url') }}/images/kreios-logo.jpg" alt="Krèios" style="height: 40px;">
</x-mail::header>
</x-slot:header>
# Nuovo preventivo n. {{ $preventivo->numero }}
È arrivata una nuova richiesta di preventivo dal configuratore.
**Cliente:** {{ $preventivo->cliente?->nominativo }}
**Email:** {{ $preventivo->cliente?->email }}
**Telefono:** {{ $preventivo->cliente?->telefono ?: '—' }}
**Prodotti:** {{ $preventivo->items->count() }}
**Totale:** {{ number_format($preventivo->totale, 2, ',', '.') }}
<x-mail::button :url="$adminUrl">
Apri nel backoffice
</x-mail::button>
Krèios
</x-mail::message>
@@ -0,0 +1,23 @@
<x-mail::message>
<x-slot:header>
<x-mail::header :url="config('app.url')">
<img src="{{ config('app.url') }}/images/kreios-logo.jpg" alt="Krèios" style="height: 40px;">
</x-mail::header>
</x-slot:header>
# Grazie per la tua richiesta
Ciao {{ $preventivo->cliente?->nome }},
abbiamo ricevuto la tua richiesta di preventivo **n. {{ $preventivo->numero }}**.
In allegato trovi il **PDF riassuntivo** con le configurazioni che hai selezionato e il totale
indicativo (IVA 22% inclusa).
Un nostro consulente ti ricontatterà al più presto per finalizzare l'offerta.
**Totale indicativo:** {{ number_format($preventivo->totale, 2, ',', '.') }}
Grazie,<br>
Il team di Krèios
</x-mail::message>
@@ -0,0 +1,105 @@
@php
use App\Support\ConfigurazioneFormatter;
use Illuminate\Support\Facades\Storage;
$cliente = $preventivo->cliente;
$logoPath = public_path('images/kreios-logo.jpg');
$logoBase64 = file_exists($logoPath) ? base64_encode(file_get_contents($logoPath)) : null;
@endphp
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="utf-8">
<style>
* { font-family: DejaVu Sans, sans-serif; }
body { color: #222; font-size: 12px; margin: 0; }
.header { border-bottom: 3px solid #12373C; padding-bottom: 10px; margin-bottom: 18px; }
.header table { width: 100%; }
.header .logo { width: 70px; vertical-align: middle; }
.header .logo img { width: 60px; height: auto; }
.header h1 { color: #12373C; margin: 0; font-size: 22px; }
.header .meta { color: #666; font-size: 11px; margin-top: 4px; }
h2 { color: #12373C; font-size: 14px; border-bottom: 1px solid #ddd; padding-bottom: 4px; margin: 18px 0 8px; }
table { width: 100%; border-collapse: collapse; }
.cliente td { padding: 3px 6px; vertical-align: top; }
.cliente td.k { color: #666; width: 130px; }
.item { border: 1px solid #ddd; border-radius: 4px; margin-bottom: 12px; padding: 10px; page-break-inside: avoid; }
.item .titolo { font-weight: bold; font-size: 13px; color: #12373C; margin-bottom: 6px; }
.item .body { }
.item .foto { width: 150px; vertical-align: top; padding-right: 12px; }
.item .foto img { width: 150px; height: auto; border: 1px solid #eee; }
.conf td { padding: 2px 6px; border-bottom: 1px solid #f0f0f0; }
.conf td.k { color: #666; width: 45%; }
.prezzo-riga { margin-top: 6px; text-align: right; font-size: 12px; }
.totale { margin-top: 14px; text-align: right; font-size: 16px; font-weight: bold; color: #12373C; }
.nota { color: #888; font-size: 10px; text-align: right; }
</style>
</head>
<body>
<div class="header">
<table>
<tr>
@if($logoBase64)
<td class="logo"><img src="data:image/jpeg;base64,{{ $logoBase64 }}" alt="Krèios"></td>
@endif
<td>
<h1>Preventivo n. {{ $preventivo->numero }}</h1>
<div class="meta">
Krèios Infissi &middot; Emesso il {{ optional($preventivo->confirmed_at)->format('d/m/Y') ?? now()->format('d/m/Y') }}
</div>
</td>
</tr>
</table>
</div>
<h2>Dati cliente</h2>
<table class="cliente">
<tr><td class="k">Nominativo</td><td>{{ $cliente?->nominativo }}</td></tr>
@if($cliente?->ragione_sociale)
<tr><td class="k">Ragione sociale</td><td>{{ $cliente->ragione_sociale }}</td></tr>
@endif
<tr><td class="k">Email</td><td>{{ $cliente?->email }}</td></tr>
<tr><td class="k">Telefono</td><td>{{ $cliente?->telefono ?: '—' }}</td></tr>
<tr><td class="k">Indirizzo</td><td>{{ trim(($cliente?->indirizzo ?: '').' '.($cliente?->cap ?: '').' '.($cliente?->citta ?: '').' '.($cliente?->provincia ? '('.$cliente->provincia.')' : '')) ?: '—' }}</td></tr>
@if($cliente?->note)
<tr><td class="k">Note</td><td>{{ $cliente->note }}</td></tr>
@endif
</table>
<h2>Configurazioni ({{ $preventivo->items->count() }})</h2>
@foreach($preventivo->items as $i => $item)
@php
$imgAbs = $item->immagine_path && Storage::disk('public')->exists($item->immagine_path)
? Storage::disk('public')->path($item->immagine_path) : null;
$rows = ConfigurazioneFormatter::toRows($item->payload);
@endphp
<div class="item">
<div class="titolo">{{ $i + 1 }}. {{ $item->nome_modello ?: 'Prodotto' }}
@if($item->sistema) <span style="color:#888;font-weight:normal;">({{ ucfirst($item->sistema) }})</span> @endif
</div>
<table class="body">
<tr>
@if($imgAbs)
<td class="foto">
<img src="data:image/png;base64,{{ base64_encode(file_get_contents($imgAbs)) }}" alt="">
</td>
@endif
<td>
<table class="conf">
@foreach($rows as $row)
<tr><td class="k">{{ $row['label'] }}</td><td>{{ $row['value'] }}</td></tr>
@endforeach
</table>
</td>
</tr>
</table>
<div class="prezzo-riga">
Quantità: {{ $item->quantita }} &nbsp;&times;&nbsp; {{ number_format($item->prezzo_unitario, 2, ',', '.') }}
= <strong> {{ number_format($item->prezzo_totale, 2, ',', '.') }}</strong>
</div>
</div>
@endforeach
<div class="totale">Totale: {{ number_format($preventivo->totale, 2, ',', '.') }}</div>
<div class="nota">IVA 22% inclusa. Preventivo indicativo, salvo verifica tecnica e rilievo misure.</div>
</body>
</html>
@@ -0,0 +1,24 @@
@props([
'url',
'color' => 'primary',
'align' => 'center',
])
<table class="action" align="{{ $align }}" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="{{ $align }}">
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="{{ $align }}">
<table border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td>
<a href="{{ $url }}" class="button button-{{ $color }}" target="_blank" rel="noopener">{!! $slot !!}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
@@ -0,0 +1,11 @@
<tr>
<td>
<table class="footer" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="content-cell" align="center">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
</td>
</tr>
@@ -0,0 +1,12 @@
@props(['url'])
<tr>
<td class="header">
<a href="{{ $url }}" style="display: inline-block;">
@if (trim($slot) === 'Laravel')
<img src="https://laravel.com/img/notification-logo-v2.1.png" class="logo" alt="Laravel Logo">
@else
{!! $slot !!}
@endif
</a>
</td>
</tr>
@@ -0,0 +1,58 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<title>{{ config('app.name') }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="color-scheme" content="light">
<meta name="supported-color-schemes" content="light">
<style>
@media only screen and (max-width: 600px) {
.inner-body {
width: 100% !important;
}
.footer {
width: 100% !important;
}
}
@media only screen and (max-width: 500px) {
.button {
width: 100% !important;
}
}
</style>
{!! $head ?? '' !!}
</head>
<body>
<table class="wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table class="content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
{!! $header ?? '' !!}
<!-- Email Body -->
<tr>
<td class="body" width="100%" cellpadding="0" cellspacing="0" style="border: hidden !important;">
<table class="inner-body" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
<!-- Body content -->
<tr>
<td class="content-cell">
{!! Illuminate\Mail\Markdown::parse($slot) !!}
{!! $subcopy ?? '' !!}
</td>
</tr>
</table>
</td>
</tr>
{!! $footer ?? '' !!}
</table>
</td>
</tr>
</table>
</body>
</html>
@@ -0,0 +1,27 @@
<x-mail::layout>
{{-- Header --}}
<x-slot:header>
<x-mail::header :url="config('app.url')">
{{ config('app.name') }}
</x-mail::header>
</x-slot:header>
{{-- Body --}}
{!! $slot !!}
{{-- Subcopy --}}
@isset($subcopy)
<x-slot:subcopy>
<x-mail::subcopy>
{!! $subcopy !!}
</x-mail::subcopy>
</x-slot:subcopy>
@endisset
{{-- Footer --}}
<x-slot:footer>
<x-mail::footer>
© {{ date('Y') }} {{ config('app.name') }}. {{ __('All rights reserved.') }}
</x-mail::footer>
</x-slot:footer>
</x-mail::layout>
@@ -0,0 +1,14 @@
<table class="panel" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="panel-content">
<table width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="panel-item">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
</td>
</tr>
</table>
@@ -0,0 +1,7 @@
<table class="subcopy" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td>
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
@@ -0,0 +1,3 @@
<div class="table">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</div>
@@ -0,0 +1,297 @@
/* Base */
body,
body *:not(html):not(style):not(br):not(tr):not(code) {
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
position: relative;
}
body {
-webkit-text-size-adjust: none;
background-color: #ffffff;
color: #52525b;
height: 100%;
line-height: 1.4;
margin: 0;
padding: 0;
width: 100% !important;
}
p,
ul,
ol,
blockquote {
line-height: 1.4;
text-align: start;
}
a {
color: #18181b;
}
a img {
border: none;
}
/* Typography */
h1 {
color: #18181b;
font-size: 18px;
font-weight: bold;
margin-top: 0;
text-align: start;
}
h2 {
font-size: 16px;
font-weight: bold;
margin-top: 0;
text-align: start;
}
h3 {
font-size: 14px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
p {
font-size: 16px;
line-height: 1.5em;
margin-top: 0;
text-align: left;
}
p.sub {
font-size: 12px;
}
img {
max-width: 100%;
}
/* Layout */
.wrapper {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
background-color: #fafafa;
margin: 0;
padding: 0;
width: 100%;
}
.content {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 0;
padding: 0;
width: 100%;
}
/* Header */
.header {
padding: 25px 0;
text-align: center;
}
.header a {
color: #18181b;
font-size: 19px;
font-weight: bold;
text-decoration: none;
}
/* Logo */
.logo {
height: 75px;
margin-top: 15px;
margin-bottom: 10px;
max-height: 75px;
width: 75px;
}
/* Body */
.body {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
background-color: #fafafa;
border-bottom: 1px solid #fafafa;
border-top: 1px solid #fafafa;
margin: 0;
padding: 0;
width: 100%;
}
.inner-body {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
background-color: #ffffff;
border-color: #e4e4e7;
border-radius: 4px;
border-width: 1px;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
margin: 0 auto;
padding: 0;
width: 570px;
}
.inner-body a {
word-break: break-all;
}
/* Subcopy */
.subcopy {
border-top: 1px solid #e4e4e7;
margin-top: 25px;
padding-top: 25px;
}
.subcopy p {
font-size: 14px;
}
/* Footer */
.footer {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
margin: 0 auto;
padding: 0;
text-align: center;
width: 570px;
}
.footer p {
color: #a1a1aa;
font-size: 12px;
text-align: center;
}
.footer a {
color: #a1a1aa;
text-decoration: underline;
}
/* Tables */
.table table {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 30px auto;
width: 100%;
}
.table th {
border-bottom: 1px solid #e4e4e7;
margin: 0;
padding-bottom: 8px;
}
.table td {
color: #52525b;
font-size: 15px;
line-height: 18px;
margin: 0;
padding: 10px 0;
}
.content-cell {
max-width: 100vw;
padding: 32px;
}
/* Buttons */
.action {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 30px auto;
padding: 0;
text-align: center;
width: 100%;
float: unset;
}
.button {
-webkit-text-size-adjust: none;
border-radius: 4px;
color: #fff;
display: inline-block;
overflow: hidden;
text-decoration: none;
}
.button-blue,
.button-primary {
background-color: #12373C;
border-bottom: 8px solid #12373C;
border-left: 18px solid #12373C;
border-right: 18px solid #12373C;
border-top: 8px solid #12373C;
}
.button-green,
.button-success {
background-color: #16a34a;
border-bottom: 8px solid #16a34a;
border-left: 18px solid #16a34a;
border-right: 18px solid #16a34a;
border-top: 8px solid #16a34a;
}
.button-red,
.button-error {
background-color: #dc2626;
border-bottom: 8px solid #dc2626;
border-left: 18px solid #dc2626;
border-right: 18px solid #dc2626;
border-top: 8px solid #dc2626;
}
/* Panels */
.panel {
border-left: #18181b solid 4px;
margin: 21px 0;
}
.panel-content {
background-color: #fafafa;
color: #52525b;
padding: 16px;
}
.panel-content p {
color: #52525b;
}
.panel-item {
padding: 0;
}
.panel-item p:last-of-type {
margin-bottom: 0;
padding-bottom: 0;
}
/* Utilities */
.break-all {
word-break: break-all;
}
@@ -0,0 +1 @@
{{ $slot }}: {{ $url }}
@@ -0,0 +1 @@
{{ $slot }}
@@ -0,0 +1 @@
{{ $slot }}: {{ $url }}
@@ -0,0 +1,9 @@
{!! strip_tags($header ?? '') !!}
{!! strip_tags($slot) !!}
@isset($subcopy)
{!! strip_tags($subcopy) !!}
@endisset
{!! strip_tags($footer ?? '') !!}
@@ -0,0 +1,27 @@
<x-mail::layout>
{{-- Header --}}
<x-slot:header>
<x-mail::header :url="config('app.url')">
{{ config('app.name') }}
</x-mail::header>
</x-slot:header>
{{-- Body --}}
{{ $slot }}
{{-- Subcopy --}}
@isset($subcopy)
<x-slot:subcopy>
<x-mail::subcopy>
{{ $subcopy }}
</x-mail::subcopy>
</x-slot:subcopy>
@endisset
{{-- Footer --}}
<x-slot:footer>
<x-mail::footer>
© {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.')
</x-mail::footer>
</x-slot:footer>
</x-mail::layout>
@@ -0,0 +1 @@
{{ $slot }}
@@ -0,0 +1 @@
{{ $slot }}
@@ -0,0 +1 @@
{{ $slot }}
File diff suppressed because one or more lines are too long