Sfoglia il codice sorgente

Saltacoda: pre-ordine cliente, token e import in cassa.

Dopo il pagamento il pre-ordine passa a importato e conta in fatturazione.

Co-authored-by: Cursor <cursoragent@cursor.com>
marcofalabretti 2 settimane fa
parent
commit
ab10331a4b

+ 334
- 60
app/Http/Controllers/SaltacodaController.php Vedi File

@@ -2,16 +2,20 @@
2 2
 
3 3
 namespace App\Http\Controllers;
4 4
 
5
+
6
+use Illuminate\Routing\Controllers\Middleware;
5 7
 use Illuminate\Http\Request;
6 8
 use App\Models\Attivita;
7 9
 use App\Models\Cucina;
8 10
 use App\Models\Piatto;
9 11
 use App\Models\Saltacoda;
10 12
 use App\Models\SaltacodaOrdine;
11
-use App\Models\SaltacodaRigaordine;
13
+use App\Models\SaltacodaRigaOrdine;
12 14
 use App\Models\VariantePiatto;
13 15
 use App\Models\SaltacodaRigaOrdineHasVariante;
14 16
 use Illuminate\Support\Facades\DB;
17
+use Illuminate\Support\Str;
18
+use App\Services\Attivita\AttivitaService;
15 19
 
16 20
 
17 21
 class SaltacodaController extends Controller
@@ -28,7 +32,7 @@ class SaltacodaController extends Controller
28 32
     {
29 33
         return [
30 34
             new Middleware('permission:view-saltacoda', only: ['index']),
31
-            new Middleware('permission:create-saltacoda|edit-saltacoda|delete-saltacoda', only: ['store', 'update', 'destroy']),
35
+            new Middleware('permission:create-saltacoda|edit-saltacoda|delete-saltacoda', only: ['store', 'update', 'destroy', 'attiva']),
32 36
         ];
33 37
     }
34 38
 
@@ -76,6 +80,8 @@ class SaltacodaController extends Controller
76 80
             'istruzioni' => 'nullable|json',
77 81
         ]);
78 82
 
83
+        AttivitaService::garantisciAttivita($request->attivita_id);
84
+
79 85
         $saltacoda = Saltacoda::updateOrCreate(
80 86
             ['attivita_id' => $request->attivita_id],
81 87
             [
@@ -90,9 +96,11 @@ class SaltacodaController extends Controller
90 96
         return redirect()->route('saltacoda.index')->with('success', 'Saltacoda salvato');
91 97
     }
92 98
 
93
-    public function show(Request $request)
99
+
100
+    ///METODI CLIENTE NON AUTENTICATI
101
+    public function show(Request $request, $slug)
94 102
     {
95
-        $attivita = Attivita::find($request->get('attivita_id'));
103
+        $attivita = Attivita::where('slug', $slug)->first();
96 104
         if(!$attivita) {
97 105
             return response()->json([
98 106
                 'success' => false,
@@ -176,6 +184,21 @@ class SaltacodaController extends Controller
176 184
             }
177 185
         }
178 186
 
187
+        $ordineInModifica = null;
188
+        $avvisiModifica = null;
189
+        if ($request->filled('codice')) {
190
+            $preOrdine = $this->trovaPreOrdineModificabile(
191
+                $request->get('codice'),
192
+                (int) $attivita->id,
193
+                false,
194
+                $this->tokenCliente($request)
195
+            );
196
+            if ($preOrdine) {
197
+                $avvisiModifica = $this->analizzaOrdineNonValido($preOrdine);
198
+                $ordineInModifica = $this->serializzaPreOrdinePerModifica($preOrdine);
199
+            }
200
+        }
201
+
179 202
         return view('saltacoda.show', [
180 203
             'attivita' => $attivita,
181 204
             'cucine' => $cucine,
@@ -183,11 +206,13 @@ class SaltacodaController extends Controller
183 206
             'saltacoda_id' => $saltacoda->id,
184 207
             'varianti_per_piatto' => $variantiPerPiatto,
185 208
             'varianti_per_cucina' => $variantiPerCucina,
209
+            'ordine_in_modifica' => $ordineInModifica,
210
+            'avvisi_modifica' => $avvisiModifica,
186 211
         ]);
187 212
     }
188 213
 
189 214
     public function pre_ordine_salva(Request $request)
190
-    {
215
+    {       
191 216
         $request->validate([
192 217
             'saltacoda_id' => 'required|exists:saltacoda,id',
193 218
             'attivita_id' => 'required|exists:attivita,id',
@@ -199,6 +224,8 @@ class SaltacodaController extends Controller
199 224
             'riga_ordine.*.varianti.*' => 'integer|exists:variante_piatto,id',
200 225
             'info_cliente' => 'nullable|array',
201 226
             'idempotency_key' => 'nullable|string|max:120',
227
+            'codice' => 'nullable|string|max:9',
228
+            't' => 'nullable|string|max:64',
202 229
         ]);
203 230
 
204 231
         $saltacoda = Saltacoda::where('id', $request->saltacoda_id)
@@ -211,18 +238,37 @@ class SaltacodaController extends Controller
211 238
             ], 422);
212 239
         }
213 240
 
241
+        $isModifica = $request->filled('codice');
242
+
214 243
         try {
215
-            $saltacoda_ordine = DB::transaction(function () use ($request) {
216
-                $saltacoda_ordine = SaltacodaOrdine::firstOrNew([
217
-                    'riferimento' => $request->idempotency_key,
218
-                ]);
244
+            $saltacoda_ordine = DB::transaction(function () use ($request, $isModifica) {
245
+                if ($isModifica) {
246
+                    $saltacoda_ordine = $this->trovaPreOrdineModificabile(
247
+                        $request->codice,
248
+                        (int) $request->attivita_id,
249
+                        true,
250
+                        $this->tokenCliente($request)
251
+                    );
252
+
253
+                    if (!$saltacoda_ordine) {
254
+                        throw new \InvalidArgumentException('Questo pre-ordine non può più essere modificato.');
255
+                    }
256
+                } else {
257
+                    $saltacoda_ordine = SaltacodaOrdine::firstOrNew([
258
+                        'riferimento' => $request->idempotency_key,
259
+                    ]);
260
+
261
+                    if (!$saltacoda_ordine->exists) {
262
+                        $saltacoda_ordine->codice = $this->genera_codice_saltacoda();
263
+                        $saltacoda_ordine->token = Str::random(32);
264
+                    } elseif (! filled($saltacoda_ordine->token)) {
265
+                        $saltacoda_ordine->token = Str::random(32);
266
+                    }
219 267
 
220
-                if (!$saltacoda_ordine->exists) {
221
-                    $saltacoda_ordine->codice = $this->genera_codice_saltacoda();
268
+                    $saltacoda_ordine->riferimento = $request->idempotency_key;
222 269
                 }
223 270
 
224 271
                 $saltacoda_ordine->fill([
225
-                    'riferimento' => $request->idempotency_key,
226 272
                     'attivita_id' => $request->attivita_id,
227 273
                     'info' => $request->info_cliente,
228 274
                     'prenotazione_id' => null,
@@ -236,49 +282,7 @@ class SaltacodaController extends Controller
236 282
                 $saltacoda_ordine->save();
237 283
 
238 284
                 $saltacoda_ordine->saltacoda_riga_ordine()->delete();
239
-
240
-                foreach ($request->riga_ordine as $riga) {
241
-                    $piatto = Piatto::query()
242
-                        ->where('id', $riga['piatto_id'])
243
-                        ->where('attivita_id', $request->attivita_id)
244
-                        ->first();
245
-
246
-                    if (!$piatto) {
247
-                        throw new \InvalidArgumentException('Piatto non valido per questa attività.');
248
-                    }
249
-
250
-                    $qta = (int) $riga['qta'];
251
-                    $varianteIds = collect($riga['varianti'] ?? [])->unique()->values();
252
-                    $varianti = $varianteIds->isEmpty()
253
-                        ? collect()
254
-                        : VariantePiatto::query()
255
-                            ->where('attivita_id', $request->attivita_id)
256
-                            ->where('is_attivo', true)
257
-                            ->whereIn('id', $varianteIds)
258
-                            ->get();
259
-
260
-                    if ($varianteIds->count() !== $varianti->count()) {
261
-                        throw new \InvalidArgumentException('Una o più varianti non sono valide per questa attività.');
262
-                    }
263
-
264
-                    $prezzoRiga = $this->calcolaPrezzoRigaSaltacoda($piatto, $qta, $varianti);
265
-
266
-                    $saltacoda_riga = SaltacodaRigaordine::create([
267
-                        'saltacoda_ordine_id' => $saltacoda_ordine->id,
268
-                        'piatto_id' => $piatto->id,
269
-                        'quantita' => $qta,
270
-                        'note' => $riga['note'] ?? null,
271
-                        'prezzo' => $prezzoRiga,
272
-                    ]);
273
-
274
-                    foreach ($varianti as $variante) {
275
-                        SaltacodaRigaOrdineHasVariante::create([
276
-                            'saltacoda_riga_ordine_id' => $saltacoda_riga->id,
277
-                            'variante_id' => $variante->id,
278
-                            'nota' => null,
279
-                        ]);
280
-                    }
281
-                }
285
+                $this->salvaRighePreOrdine($saltacoda_ordine, $request);
282 286
 
283 287
                 $saltacoda_ordine->prezzo = $saltacoda_ordine->saltacoda_riga_ordine()->sum('prezzo');
284 288
                 $saltacoda_ordine->save();
@@ -289,11 +293,29 @@ class SaltacodaController extends Controller
289 293
                 ]);
290 294
             });
291 295
 
292
-            return response()->json([
296
+            $json = response()->json([
293 297
                 'success' => true,
294
-                'message' => 'Pre-ordine salvato',
298
+                'message' => $isModifica ? 'Pre-ordine aggiornato' : 'Pre-ordine salvato',
295 299
                 'data' => $saltacoda_ordine,
300
+                'token' => $saltacoda_ordine->token,
296 301
             ]);
302
+
303
+            if (filled($saltacoda_ordine->token)) {
304
+                $json->cookie(
305
+                    'saltacoda_token',
306
+                    $saltacoda_ordine->token,
307
+                    60 * 12,
308
+                    '/',
309
+                    null,
310
+                    false,
311
+                    true,
312
+                    false,
313
+                    'lax'
314
+                );
315
+            }
316
+
317
+            return $json;
318
+
297 319
         } catch (\InvalidArgumentException $exception) {
298 320
             return response()->json([
299 321
                 'success' => false,
@@ -304,18 +326,270 @@ class SaltacodaController extends Controller
304 326
 
305 327
     public function pre_ordine_show(Request $request)
306 328
     {
329
+        if($request->get('codice') === '' || $request->get('codice') == '' || $request->get('codice') == null) {
330
+            return abort(404);
331
+        }
332
+        $codice = SaltacodaOrdine::normalizzaCodice($request->get('codice'));
333
+        $token = $this->tokenCliente($request);
334
+        if ($codice === '' || $token === '') {
335
+            return abort(404);
336
+        }
337
+
307 338
         $saltacoda_ordine = SaltacodaOrdine::query()
308 339
             ->with([
309 340
                 'saltacoda_riga_ordine.piatto',
310 341
                 'saltacoda_riga_ordine.saltacoda_has_variante.variante_piatto',
311 342
                 'attivita',
312 343
             ])
313
-            ->where('codice', $request->get('codice'))
344
+            ->where('codice', $codice)
345
+            ->where('token', $token)
314 346
             ->first();
315 347
         if(!$saltacoda_ordine) {
316 348
             return abort(404);
317 349
         }
318
-        return view('saltacoda.pre_ordine', ['saltacoda_ordine' => $saltacoda_ordine]);
350
+
351
+        $avvisiModifica = null;
352
+        if ($saltacoda_ordine->stato === SaltacodaOrdine::PRE_ORDINE) {
353
+            $avvisiModifica = $this->analizzaOrdineNonValido($saltacoda_ordine);
354
+        }
355
+
356
+        return view('saltacoda.pre_ordine', [
357
+            'saltacoda_ordine' => $saltacoda_ordine,
358
+            'modificabile' => $saltacoda_ordine->stato === SaltacodaOrdine::PRE_ORDINE,
359
+            'avvisi_modifica' => $avvisiModifica,
360
+        ]);
361
+    }
362
+
363
+
364
+    ///METODI PRIVATI
365
+
366
+    private function tokenCliente(Request $request): string
367
+    {
368
+        return trim((string) ($request->cookie('saltacoda_token') ?: $request->input('t') ?: ''));
369
+    }
370
+
371
+    private function trovaPreOrdineModificabile(?string $codice, int $attivitaId, bool $lock = false, ?string $token = null): ?SaltacodaOrdine
372
+    {
373
+        $codice = SaltacodaOrdine::normalizzaCodice($codice);
374
+        $token = trim((string) $token);
375
+        if ($codice === '' || $token === '') {
376
+            return null;
377
+        }
378
+
379
+        $query = SaltacodaOrdine::query()
380
+            ->with([
381
+                'saltacoda_riga_ordine.piatto',
382
+                'saltacoda_riga_ordine.saltacoda_has_variante.variante_piatto',
383
+            ])
384
+            ->where('codice', $codice)
385
+            ->where('token', $token)
386
+            ->where('attivita_id', $attivitaId)
387
+            ->where('stato', SaltacodaOrdine::PRE_ORDINE);
388
+
389
+        if ($lock) {
390
+            $query->lockForUpdate();
391
+        }
392
+
393
+        return $query->first();
394
+    }
395
+
396
+    private function analizzaOrdineNonValido(SaltacodaOrdine $ordine): array
397
+    {
398
+        $ordine->loadMissing([
399
+            'saltacoda_riga_ordine.piatto',
400
+            'saltacoda_riga_ordine.saltacoda_has_variante.variante_piatto',
401
+        ]);
402
+
403
+        $piattiRimossi = [];
404
+        $variantiRimosse = [];
405
+
406
+        foreach ($ordine->saltacoda_riga_ordine as $riga) {
407
+            $piatto = $riga->piatto;
408
+            $nomePiatto = $piatto?->nome ?? ('Piatto #' . $riga->piatto_id);
409
+
410
+            if (!$piatto || !$piatto->is_attivo) {
411
+                $piattiRimossi[] = [
412
+                    'nome' => $nomePiatto,
413
+                    'qta' => (int) $riga->quantita,
414
+                ];
415
+                continue;
416
+            }
417
+
418
+            foreach ($riga->saltacoda_has_variante as $hasVariante) {
419
+                $variante = $hasVariante->variante_piatto;
420
+                if (!$variante || !$variante->is_attivo) {
421
+                    $variantiRimosse[] = [
422
+                        'piatto' => $nomePiatto,
423
+                        'label' => $variante?->label ?? 'Opzione',
424
+                    ];
425
+                }
426
+            }
427
+        }
428
+
429
+        return [
430
+            'piatti_rimossi' => $piattiRimossi,
431
+            'varianti_rimosse' => $variantiRimosse,
432
+            'ha_problemi' => $piattiRimossi !== [] || $variantiRimosse !== [],
433
+        ];
434
+    }
435
+
436
+    private function pulisciOrdineNonValido(SaltacodaOrdine $ordine): array
437
+    {
438
+        $avvisi = $this->analizzaOrdineNonValido($ordine);
439
+        if (!$avvisi['ha_problemi']) {
440
+            return $avvisi;
441
+        }
442
+
443
+        $ordine->loadMissing([
444
+            'saltacoda_riga_ordine.piatto',
445
+            'saltacoda_riga_ordine.saltacoda_has_variante.variante_piatto',
446
+        ]);
447
+
448
+        $dirty = false;
449
+
450
+        foreach ($ordine->saltacoda_riga_ordine as $riga) {
451
+            $piatto = $riga->piatto;
452
+
453
+            if (!$piatto || !$piatto->is_attivo) {
454
+                $riga->saltacoda_has_variante()->delete();
455
+                $riga->delete();
456
+                $dirty = true;
457
+                continue;
458
+            }
459
+
460
+            $rigaDirty = false;
461
+            foreach ($riga->saltacoda_has_variante as $hasVariante) {
462
+                $variante = $hasVariante->variante_piatto;
463
+                if (!$variante || !$variante->is_attivo) {
464
+                    $hasVariante->delete();
465
+                    $rigaDirty = true;
466
+                    $dirty = true;
467
+                }
468
+            }
469
+
470
+            if ($rigaDirty) {
471
+                $riga->load('saltacoda_has_variante.variante_piatto');
472
+                $variantiValide = $riga->saltacoda_has_variante
473
+                    ->map(fn ($hasVariante) => $hasVariante->variante_piatto)
474
+                    ->filter();
475
+
476
+                $riga->prezzo = $this->calcolaPrezzoRigaSaltacoda(
477
+                    $piatto,
478
+                    (int) $riga->quantita,
479
+                    $variantiValide
480
+                );
481
+                $riga->save();
482
+            }
483
+        }
484
+
485
+        if ($dirty) {
486
+            $ordine->prezzo = $ordine->saltacoda_riga_ordine()->sum('prezzo');
487
+            $ordine->save();
488
+        }
489
+
490
+        return $avvisi;
491
+    }
492
+
493
+    private function serializzaPreOrdinePerModifica(SaltacodaOrdine $ordine): array
494
+    {
495
+        $ordine->loadMissing([
496
+            'saltacoda_riga_ordine.piatto',
497
+            'saltacoda_riga_ordine.saltacoda_has_variante.variante_piatto',
498
+        ]);
499
+
500
+        $righe = [];
501
+        foreach ($ordine->saltacoda_riga_ordine as $riga) {
502
+            $piatto = $riga->piatto;
503
+            if (!$piatto || !$piatto->is_attivo) {
504
+                continue;
505
+            }
506
+
507
+            $varianti = [];
508
+            foreach ($riga->saltacoda_has_variante as $hasVariante) {
509
+                $variante = $hasVariante->variante_piatto;
510
+                if (!$variante || !$variante->is_attivo) {
511
+                    continue;
512
+                }
513
+
514
+                $varianti[] = [
515
+                    'id' => $variante->id,
516
+                    'label' => $variante->label,
517
+                    'prezzo' => (float) $variante->prezzo,
518
+                    'scope' => filled($variante->piatto_id) ? 'piatto' : 'cucina',
519
+                ];
520
+            }
521
+
522
+            $righe[] = [
523
+                'piatto_id' => $riga->piatto_id,
524
+                'cucina_id' => $piatto->cucina_id,
525
+                'nome' => $piatto->nome,
526
+                'qta' => (int) $riga->quantita,
527
+                'note' => $riga->note ?? '',
528
+                'prezzo_base' => (float) $piatto->prezzo,
529
+                'varianti' => $varianti,
530
+            ];
531
+        }
532
+
533
+        $info = $ordine->info;
534
+        if (is_string($info)) {
535
+            $decoded = json_decode($info, true);
536
+            $info = is_array($decoded) ? $decoded : [];
537
+        }
538
+        if (!is_array($info)) {
539
+            $info = [];
540
+        }
541
+
542
+        return [
543
+            'codice' => $ordine->codice,
544
+            'info_cliente' => $info,
545
+            'righe' => $righe,
546
+        ];
547
+    }
548
+
549
+    private function salvaRighePreOrdine(SaltacodaOrdine $saltacoda_ordine, Request $request): void
550
+    {
551
+        foreach ($request->riga_ordine as $riga) {
552
+            $piatto = Piatto::query()
553
+                ->where('id', $riga['piatto_id'])
554
+                ->where('attivita_id', $request->attivita_id)
555
+                ->first();
556
+
557
+            if (!$piatto) {
558
+                throw new \InvalidArgumentException('Piatto non valido per questa attività.');
559
+            }
560
+
561
+            $qta = (int) $riga['qta'];
562
+            $varianteIds = collect($riga['varianti'] ?? [])->unique()->values();
563
+            $varianti = $varianteIds->isEmpty()
564
+                ? collect()
565
+                : VariantePiatto::query()
566
+                    ->where('attivita_id', $request->attivita_id)
567
+                    ->where('is_attivo', true)
568
+                    ->whereIn('id', $varianteIds)
569
+                    ->get();
570
+
571
+            if ($varianteIds->count() !== $varianti->count()) {
572
+                throw new \InvalidArgumentException('Una o più varianti non sono valide per questa attività.');
573
+            }
574
+
575
+            $prezzoRiga = $this->calcolaPrezzoRigaSaltacoda($piatto, $qta, $varianti);
576
+
577
+            $saltacoda_riga = SaltacodaRigaOrdine::create([
578
+                'saltacoda_ordine_id' => $saltacoda_ordine->id,
579
+                'piatto_id' => $piatto->id,
580
+                'quantita' => $qta,
581
+                'note' => $riga['note'] ?? null,
582
+                'prezzo' => $prezzoRiga,
583
+            ]);
584
+
585
+            foreach ($varianti as $variante) {
586
+                SaltacodaRigaOrdineHasVariante::create([
587
+                    'saltacoda_riga_ordine_id' => $saltacoda_riga->id,
588
+                    'variante_id' => $variante->id,
589
+                    'nota' => null,
590
+                ]);
591
+            }
592
+        }
319 593
     }
320 594
 
321 595
     private function calcolaPrezzoRigaSaltacoda(Piatto $piatto, int $qta, $varianti): float
@@ -338,7 +612,7 @@ class SaltacodaController extends Controller
338 612
         $parteLettera = $lettereSicure[rand(0, count($lettereSicure) - 1)];
339 613
         $parteCifre2 = str_pad(strval(rand(0, 99)), 2, '0', STR_PAD_LEFT);
340 614
         $parteCifre3 = str_pad(strval(rand(0, 999)), 3, '0', STR_PAD_LEFT);
341
-        $codice = strtoupper($parteLettera . $parteCifre2 . '-' . $parteCifre3);
615
+        $codice = strtoupper($parteLettera . $parteCifre2 . $parteCifre3);
342 616
 
343 617
         if (SaltacodaOrdine::where('codice', $codice)->exists()) {
344 618
             return $this->genera_codice_saltacoda();

+ 1
- 1
app/Models/AbstractModels/AbstractSaltacoda.php Vedi File

@@ -70,7 +70,7 @@ abstract class AbstractSaltacoda extends \Illuminate\Foundation\Auth\User
70 70
 
71 71
     public function saltacoda_riga_ordine(): HasMany
72 72
     {
73
-        return $this->hasMany('\App\Models\SaltacodaRigaordine', 'saltacoda_ordine_id', 'id');
73
+        return $this->hasMany('\App\Models\SaltacodaRigaOrdine', 'saltacoda_ordine_id', 'id');
74 74
     }
75 75
 
76 76
 }

+ 5
- 0
app/Models/AbstractModels/AbstractSaltacodaOrdine.php Vedi File

@@ -75,6 +75,10 @@ abstract class AbstractSaltacodaOrdine extends \Illuminate\Foundation\Auth\User
75 75
         'updated_at',
76 76
     ];
77 77
     
78
+    protected $hidden = [
79
+        'token'
80
+    ];
81
+    
78 82
     public function attivita()
79 83
     {
80 84
         return $this->belongsTo('\App\Models\Attivita', 'attivita_id', 'id');
@@ -86,4 +90,5 @@ abstract class AbstractSaltacodaOrdine extends \Illuminate\Foundation\Auth\User
86 90
     }
87 91
 
88 92
 
93
+
89 94
 }

+ 1
- 1
app/Models/AbstractModels/AbstractSaltacodaRigaOrdine.php Vedi File

@@ -6,7 +6,7 @@
6 6
 
7 7
 namespace App\Models\AbstractModels;
8 8
 
9
-abstract class AbstractSaltacodaRigaordine extends \Illuminate\Foundation\Auth\User
9
+abstract class AbstractSaltacodaRigaOrdine extends \Illuminate\Foundation\Auth\User
10 10
 {
11 11
     /**  
12 12
      * The table associated with the model.

+ 15
- 0
app/Models/SaltacodaOrdine.php Vedi File

@@ -11,4 +11,19 @@ class SaltacodaOrdine extends \App\Models\AbstractModels\AbstractSaltacodaOrdine
11 11
     const PRE_CARRELLO = 'pre_carrello';
12 12
     const PRE_PROMOSSO = 'pre_promosso';
13 13
     const IMPORTATO = 'importato';
14
+
15
+    public static function normalizzaCodice($codice)
16
+    {
17
+        return preg_replace('/[^A-Z0-9]/', '', strtoupper(trim((string) $codice)));
18
+    }
19
+
20
+    public static function formattaCodice(?string $codice): string
21
+    {
22
+        $codice = self::normalizzaCodice($codice);
23
+        if ($codice === '') {
24
+            return '—';
25
+        }
26
+    
27
+        return substr($codice, 0, 3) . ' ' . substr($codice, 3);
28
+    }
14 29
 }

+ 1
- 1
app/Models/SaltacodaRigaOrdine.php Vedi File

@@ -4,7 +4,7 @@ namespace App\Models;
4 4
 
5 5
 use Illuminate\Database\Eloquent\Model;
6 6
 
7
-class SaltacodaRigaordine extends \App\Models\AbstractModels\AbstractSaltacodaRigaordine
7
+class SaltacodaRigaOrdine extends \App\Models\AbstractModels\AbstractSaltacodaRigaOrdine
8 8
 {
9 9
    
10 10
 }

+ 36
- 0
app/Observers/SaltacodaOrdineObserver.php Vedi File

@@ -0,0 +1,36 @@
1
+<?php
2
+
3
+namespace App\Observers;
4
+
5
+use App\Models\SaltacodaOrdine;
6
+use App\Services\Fatturazione\RigaFatturazioneService;
7
+
8
+class SaltacodaOrdineObserver
9
+{
10
+    public function created(SaltacodaOrdine $saltacodaOrdine): void
11
+    {
12
+        //
13
+    }
14
+
15
+    public function updated(SaltacodaOrdine $saltacodaOrdine): void
16
+    {
17
+        if($saltacodaOrdine->wasChanged('stato') && $saltacodaOrdine->stato == SaltacodaOrdine::IMPORTATO){
18
+            app('\App\Services\Fatturazione\RigaFatturazioneService')->aggiornaUtilizzoSaltacoda($saltacodaOrdine);
19
+        }
20
+    }
21
+
22
+    public function deleted(SaltacodaOrdine $saltacodaOrdine): void
23
+    {
24
+        //
25
+    }
26
+
27
+    public function restored(SaltacodaOrdine $saltacodaOrdine): void
28
+    {
29
+        //
30
+    }
31
+
32
+    public function forceDeleted(SaltacodaOrdine $saltacodaOrdine): void
33
+    {
34
+        //
35
+    }
36
+}

+ 29
- 0
database/migrations/2026_08_29_153634_saltacodaOrdine_token.php Vedi File

@@ -0,0 +1,29 @@
1
+<?php
2
+
3
+use Illuminate\Database\Migrations\Migration;
4
+use Illuminate\Database\Schema\Blueprint;
5
+use Illuminate\Support\Facades\Schema;
6
+
7
+return new class extends Migration
8
+{
9
+    /**
10
+     * Run the migrations.
11
+     */
12
+    public function up(): void
13
+    {
14
+        Schema::table('saltacoda_ordine', function (Blueprint $table) {
15
+            $table->string('token',64)->nullable()->unique()->after('info');
16
+        });
17
+    }
18
+
19
+    /**
20
+     * Reverse the migrations.
21
+     */
22
+    public function down(): void
23
+    {
24
+        Schema::table('saltacoda_ordine', function (Blueprint $table) {
25
+            $table->dropUnique(['token']);
26
+            $table->dropColumn('token');
27
+        });
28
+    }
29
+};

+ 696
- 326
resources/views/saltacoda/_partials/cliente/fest-layout-style.blade.php
File diff soppresso perché troppo grande
Vedi File


+ 1
- 1
resources/views/saltacoda/index.blade.php Vedi File

@@ -39,7 +39,7 @@ $configData = Helper::appClasses();
39 39
 
40 40
 @if(isset($saltacoda))
41 41
 @php
42
-  $clienteUrl = route('cliente.saltacoda.show', ['attivita_id' => $attivita->id]);
42
+  $clienteUrl = route('cliente.saltacoda.show', ['slug' => $attivita->slug]);
43 43
   $stats = [
44 44
     'pre_ordini_in_coda' => SaltacodaOrdine::query()
45 45
       ->where('attivita_id', $attivita->id)

+ 432
- 220
resources/views/saltacoda/pre_ordine.blade.php Vedi File

@@ -9,10 +9,106 @@
9 9
   $festLogoLarge = asset('assets/img/logo_fest_L.png');
10 10
   $festLogoFallback = Attivita::defaultLogoUrl();
11 11
   $backUrl = $attivita
12
-    ? route('cliente.attivita.show', ['attivita_id' => $attivita->id])
12
+    ? route('cliente.attivita.show', ['slug' => $attivita->slug])
13 13
     : (Route::has('welcome') ? route('welcome') : url('/'));
14
-  $istruzioni = Saltacoda::where('attivita_id', $saltacoda_ordine->attivita_id)->first()?->getIstruzioni()
15
-    ?? Saltacoda::defaultIstruzioni();
14
+  $menuUrl = $attivita
15
+    ? route('cliente.saltacoda.show', ['slug' => $attivita->slug])
16
+    : $backUrl;
17
+  $modificabile = ($modificabile ?? false) && $attivita && filled($saltacoda_ordine->codice ?? null);
18
+  $modificaUrl = $modificabile
19
+    ? route('cliente.saltacoda.show', array_filter([
20
+        'slug' => $attivita->slug,
21
+        'codice' => $saltacoda_ordine->codice,
22
+        't' => request('t'),
23
+      ]))
24
+    : null;
25
+  $saltacodaConfig = Saltacoda::where('attivita_id', $saltacoda_ordine->attivita_id)->first();
26
+  $istruzioni = $saltacodaConfig?->getIstruzioni() ?? Saltacoda::defaultIstruzioni();
27
+
28
+  $campiInfoConfig = [];
29
+  $rawInfoConfig = $saltacodaConfig?->info;
30
+  if (is_string($rawInfoConfig) && $rawInfoConfig !== '') {
31
+      $decodedConfig = json_decode($rawInfoConfig, true);
32
+      if (is_array($decodedConfig)) {
33
+          foreach ($decodedConfig as $key => $value) {
34
+              if (is_string($value)) {
35
+                  $campiInfoConfig[] = [
36
+                      'key' => (string) $key,
37
+                      'label' => $value,
38
+                      'type' => 'text',
39
+                  ];
40
+                  continue;
41
+              }
42
+              if (is_array($value)) {
43
+                  $type = $value['type'] ?? 'text';
44
+                  if (! in_array($type, ['text', 'boolean', 'time'], true)) {
45
+                      $type = 'text';
46
+                  }
47
+                  $campiInfoConfig[] = [
48
+                      'key' => (string) $key,
49
+                      'label' => (string) ($value['label'] ?? $key),
50
+                      'type' => $type,
51
+                  ];
52
+              }
53
+          }
54
+      }
55
+  }
56
+
57
+  $infoCliente = $saltacoda_ordine->info;
58
+  if (is_string($infoCliente)) {
59
+      $decodedInfo = json_decode($infoCliente, true);
60
+      $infoCliente = is_array($decodedInfo) ? $decodedInfo : [];
61
+  }
62
+  if (! is_array($infoCliente)) {
63
+      $infoCliente = [];
64
+  }
65
+
66
+  $formatInfoValue = static function (string $type, $value): string {
67
+      $value = trim((string) ($value ?? ''));
68
+      if ($value === '') {
69
+          return '—';
70
+      }
71
+      if ($type === 'boolean') {
72
+          return match ($value) {
73
+              'si' => 'Sì',
74
+              'no' => 'No',
75
+              default => $value,
76
+          };
77
+      }
78
+      return $value;
79
+  };
80
+
81
+  $infoClienteRows = [];
82
+  $infoKeysMostrati = [];
83
+  foreach ($campiInfoConfig as $field) {
84
+      $rawValue = $infoCliente[$field['key']] ?? null;
85
+      $value = trim((string) ($rawValue ?? ''));
86
+      if ($value === '') {
87
+          continue;
88
+      }
89
+      $infoKeysMostrati[] = $field['key'];
90
+      $infoClienteRows[] = [
91
+          'label' => $field['label'],
92
+          'value' => $formatInfoValue($field['type'], $value),
93
+      ];
94
+  }
95
+  foreach ($infoCliente as $key => $val) {
96
+      if (in_array((string) $key, $infoKeysMostrati, true)) {
97
+          continue;
98
+      }
99
+      $value = trim((string) ($val ?? ''));
100
+      if ($value === '') {
101
+          continue;
102
+      }
103
+      $infoClienteRows[] = [
104
+          'label' => ucfirst(str_replace('_', ' ', (string) $key)),
105
+          'value' => $formatInfoValue('text', $value),
106
+      ];
107
+  }
108
+
109
+  $righeOrdine = $saltacoda_ordine->saltacoda_riga_ordine ?? $saltacoda_ordine->riga_ordine ?? collect();
110
+  $totalePiatti = (int) $righeOrdine->sum('quantita');
111
+  $countLabel = $totalePiatti === 1 ? '1 piatto' : $totalePiatti . ' piatti';
16 112
 @endphp
17 113
 
18 114
 @extends('layouts/guest')
@@ -23,7 +119,7 @@
23 119
   <script>document.documentElement.classList.add('saltacoda-cliente-page');</script>
24 120
   <link rel="preconnect" href="https://fonts.googleapis.com" />
25 121
   <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
26
-  <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,400;0,500;0,600;0,700;0,800;1,400&display=swap" rel="stylesheet" />
122
+  <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@500;600;700;800&family=Plus+Jakarta+Sans:ital,wght@0,400;0,500;0,600;0,700;0,800;1,400&display=swap" rel="stylesheet" />
27 123
   <meta name="theme-color" content="{{ $accent }}">
28 124
   <meta name="theme-color" media="(prefers-color-scheme: light)" content="{{ $accent }}">
29 125
   <meta name="theme-color" media="(prefers-color-scheme: dark)" content="{{ $accent }}">
@@ -36,8 +132,9 @@
36 132
 @section('page-style')
37 133
   @include('saltacoda._partials.cliente.fest-layout-style')
38 134
 <style>
39
-  .saltacoda-preordine {
40
-    --saltacoda-topbar-h: 72px;
135
+  .sc-preordine-page {
136
+    --saltacoda-topbar-h: 60px;
137
+    --saltacoda-topbar-offset: calc(var(--saltacoda-topbar-h) + env(safe-area-inset-top, 0px));
41 138
     position: relative;
42 139
     z-index: 1;
43 140
     min-height: 100vh;
@@ -47,211 +144,267 @@
47 144
     background: transparent;
48 145
   }
49 146
 
50
-  .saltacoda-preordine-topbar {
51
-    height: var(--saltacoda-topbar-h);
147
+  .sc-preordine-topbar {
148
+    min-height: var(--saltacoda-topbar-offset);
149
+    height: auto;
150
+    box-sizing: border-box;
52 151
   }
53 152
 
54
-  .saltacoda-preordine-content {
55
-    margin-top: var(--saltacoda-topbar-h);
56
-    padding: 0.8rem 0.75rem calc(1rem + env(safe-area-inset-bottom));
57
-    min-height: calc(100vh - var(--saltacoda-topbar-h));
58
-    min-height: calc(100dvh - var(--saltacoda-topbar-h));
153
+  .sc-preordine-topbar.saltacoda-fest-topbar {
154
+    justify-content: flex-start;
59 155
   }
60 156
 
61
-  .saltacoda-section {
62
-    background: #fff;
63
-    border: 1px solid color-mix(in srgb, var(--text-main) 8%, transparent);
64
-    border-radius: 1rem;
65
-    box-shadow: 0 5px 14px rgba(21, 38, 92, 0.04);
66
-    padding: 0.95rem;
67
-    margin-bottom: 0.85rem;
157
+  .sc-preordine-topbar .saltacoda-topbar-heading {
158
+    flex: 1;
159
+    min-width: 0;
160
+    text-align: center;
161
+    padding: 0 0.25rem;
68 162
   }
69 163
 
70
-  .saltacoda-section-title {
71
-    margin: 0 0 0.7rem;
72
-    font-size: 0.83rem;
73
-    font-weight: 700;
74
-    letter-spacing: 0.02em;
75
-    text-transform: uppercase;
164
+  .sc-preordine-topbar .saltacoda-topbar-title {
165
+    margin: 0;
166
+    font-size: 1rem;
167
+    font-weight: 800;
168
+    color: var(--text-main);
169
+    line-height: 1.1;
170
+    letter-spacing: -0.02em;
171
+  }
172
+
173
+  .sc-preordine-topbar .saltacoda-topbar-subtitle {
174
+    margin: 0.12rem 0 0;
175
+    font-size: 0.72rem;
176
+    font-weight: 600;
76 177
     color: var(--text-muted);
178
+    line-height: 1.15;
179
+    white-space: nowrap;
180
+    overflow: hidden;
181
+    text-overflow: ellipsis;
77 182
   }
78 183
 
79
-  .saltacoda-info-list {
80
-    display: grid;
81
-    grid-template-columns: 1fr;
82
-    gap: 0.45rem;
83
-    margin: 0;
184
+  .sc-preordine-content {
185
+    margin-top: var(--saltacoda-topbar-offset);
186
+    padding: 0.85rem 0.85rem calc(1.35rem + env(safe-area-inset-bottom));
187
+    min-height: calc(100dvh - var(--saltacoda-topbar-offset));
188
+    scroll-behavior: smooth;
84 189
   }
85 190
 
86
-  .saltacoda-info-row {
87
-    display: flex;
88
-    justify-content: space-between;
89
-    align-items: center;
90
-    gap: 0.65rem;
191
+  .sc-preordine-content > * {
192
+    animation: scPreordineIn 0.38s var(--fest-ease) both;
91 193
   }
92 194
 
93
-  .saltacoda-info-key {
94
-    color: var(--text-muted);
95
-    font-size: 0.86rem;
96
-    margin: 0;
195
+  .sc-preordine-content > *:nth-child(2) { animation-delay: 0.04s; }
196
+  .sc-preordine-content > *:nth-child(3) { animation-delay: 0.08s; }
197
+  .sc-preordine-content > *:nth-child(4) { animation-delay: 0.12s; }
198
+  .sc-preordine-content > *:nth-child(5) { animation-delay: 0.16s; }
199
+
200
+  @keyframes scPreordineIn {
201
+    from {
202
+      opacity: 0;
203
+      transform: translateY(0.45rem);
204
+    }
205
+    to {
206
+      opacity: 1;
207
+      transform: translateY(0);
208
+    }
97 209
   }
98 210
 
99
-  .saltacoda-info-value {
100
-    color: var(--text-main);
101
-    font-weight: 600;
102
-    font-size: 0.9rem;
103
-    margin: 0;
104
-    text-align: right;
211
+  @media (prefers-reduced-motion: reduce) {
212
+    .sc-preordine-content > * {
213
+      animation: none !important;
214
+    }
105 215
   }
106 216
 
107
-  .saltacoda-riga {
108
-    border: 1px solid color-mix(in srgb, var(--text-main) 8%, transparent);
217
+  .sc-preordine-content > .sc-istruzione-panel {
218
+    margin-bottom: 0.85rem;
219
+    background: #f8f9fc;
220
+    border: 1px solid var(--fest-border);
109 221
     border-radius: 0.85rem;
110
-    background: color-mix(in srgb, var(--card-accent) 4%, #fff);
111
-    padding: 0.72rem 0.78rem;
112
-    margin-bottom: 0.55rem;
222
+    padding: 0.95rem;
113 223
   }
114 224
 
115
-  .saltacoda-riga:last-child {
116
-    margin-bottom: 0;
225
+  .sc-preordine-hero {
226
+    text-align: center;
227
+    padding: 0.35rem 0.25rem 1.1rem;
228
+    margin-bottom: 0.15rem;
117 229
   }
118 230
 
119
-  .saltacoda-riga-head {
231
+  .sc-preordine-hero__mark {
232
+    width: 3.25rem;
233
+    height: 3.25rem;
234
+    margin: 0 auto 0.85rem;
235
+    border-radius: 999px;
120 236
     display: flex;
121
-    justify-content: space-between;
122 237
     align-items: center;
123
-    gap: 0.6rem;
238
+    justify-content: center;
239
+    font-size: 1.65rem;
240
+    color: var(--card-accent);
241
+    background: color-mix(in srgb, var(--card-accent) 12%, #fff);
242
+    box-shadow: 0 4px 16px color-mix(in srgb, var(--card-accent) 14%, transparent);
124 243
   }
125 244
 
126
-  .saltacoda-riga-name {
127
-    font-weight: 600;
128
-    color: var(--text-main);
245
+  .sc-preordine-hero__title {
129 246
     margin: 0;
247
+    font-family: "Outfit", "Plus Jakarta Sans", system-ui, sans-serif;
248
+    font-size: 1.35rem;
249
+    font-weight: 800;
250
+    letter-spacing: -0.03em;
251
+    color: var(--text-main);
252
+    line-height: 1.15;
130 253
   }
131 254
 
132
-  .saltacoda-riga-prezzo {
133
-    color: var(--card-accent);
134
-    font-weight: 700;
135
-    background: color-mix(in srgb, var(--card-accent) 12%, #fff);
136
-    border-radius: 999px;
137
-    padding: 0.14rem 0.55rem;
138
-    white-space: nowrap;
255
+  .sc-preordine-hero__hint {
256
+    margin: 0.35rem 0 0;
257
+    font-size: 0.86rem;
258
+    color: var(--text-muted);
259
+    line-height: 1.45;
139 260
   }
140 261
 
141
-  .saltacoda-riga-varianti {
142
-    margin: 0.35rem 0 0.45rem;
143
-    display: grid;
144
-    gap: 0.2rem;
262
+  .sc-preordine-code-card {
263
+    margin: 1rem auto 0;
264
+    max-width: 20rem;
265
+    padding: 1rem 1.1rem 1.05rem;
266
+    border-radius: 1rem;
267
+    text-align: center;
268
+    background: var(--text-main);
269
+    border: 1px solid color-mix(in srgb, var(--text-main) 88%, #000);
270
+    box-shadow:
271
+      0 10px 28px color-mix(in srgb, var(--text-main) 22%, transparent),
272
+      inset 0 1px 0 color-mix(in srgb, #fff 8%, transparent);
145 273
   }
146 274
 
147
-  .saltacoda-riga-variante {
148
-    font-size: 0.78rem;
149
-    color: var(--text-muted);
150
-    line-height: 1.35;
275
+  .sc-preordine-code-card__label {
276
+    margin: 0;
277
+    font-size: 0.72rem;
278
+    font-weight: 700;
279
+    letter-spacing: 0.06em;
280
+    text-transform: uppercase;
281
+    color: color-mix(in srgb, #fff 62%, transparent);
282
+    line-height: 1.3;
151 283
   }
152 284
 
153
-  .saltacoda-riga-variante span {
154
-    color: var(--card-accent);
155
-    font-weight: 700;
285
+  .sc-preordine-code-card__value {
286
+    margin: 0.45rem 0 0;
287
+    font-family: "Outfit", "Plus Jakarta Sans", ui-monospace, monospace, system-ui, sans-serif;
288
+    font-size: clamp(1.65rem, 7.5vw, 2rem);
289
+    font-weight: 800;
290
+    letter-spacing: 0.06em;
291
+    line-height: 1.1;
292
+    color: #fff;
293
+    font-variant-numeric: tabular-nums;
294
+    word-break: break-all;
156 295
   }
157 296
 
158
-  .saltacoda-riga-meta {
159
-    margin-top: 0.45rem;
297
+  .sc-preordine-meta {
160 298
     display: flex;
161
-    justify-content: space-between;
299
+    flex-wrap: wrap;
162 300
     align-items: center;
163
-    gap: 0.5rem;
301
+    justify-content: center;
302
+    gap: 0.35rem 0.55rem;
303
+    margin-top: 0.55rem;
164 304
   }
165 305
 
166
-  .saltacoda-riga-note {
306
+  .sc-preordine-meta__item {
307
+    font-size: 0.8rem;
308
+    font-weight: 600;
167 309
     color: var(--text-muted);
168
-    font-size: 0.82rem;
169
-    margin: 0;
170
-    word-break: break-word;
310
+    line-height: 1.3;
171 311
   }
172 312
 
173
-  .saltacoda-riga-qta {
174
-    font-size: 0.8rem;
175
-    font-weight: 700;
313
+  .sc-preordine-stato {
314
+    display: inline-flex;
315
+    align-items: center;
316
+    padding: 0.2rem 0.55rem;
176 317
     border-radius: 999px;
318
+    font-size: 0.72rem;
319
+    font-weight: 700;
320
+    color: color-mix(in srgb, var(--card-accent) 78%, var(--text-main));
177 321
     background: color-mix(in srgb, var(--card-accent) 10%, #fff);
178
-    color: var(--text-main);
179
-    padding: 0.16rem 0.45rem;
322
+    border: 1px solid color-mix(in srgb, var(--card-accent) 22%, transparent);
180 323
   }
181 324
 
182
-  .saltacoda-totali {
183
-    border-top: 1px solid color-mix(in srgb, var(--text-main) 8%, transparent);
184
-    margin-top: 0.75rem;
185
-    padding-top: 0.65rem;
186
-    display: flex;
187
-    justify-content: space-between;
188
-    align-items: flex-end;
189
-    gap: 0.6rem;
325
+  .sc-preordine-block {
326
+    margin-bottom: 0.85rem;
190 327
   }
191 328
 
192
-  .saltacoda-totale-label {
193
-    font-size: 0.82rem;
194
-    color: var(--text-muted);
195
-    margin: 0;
329
+  .sc-preordine-block__title {
330
+    margin: 0 0 0.55rem;
331
+    padding: 0 0.1rem;
332
+    font-family: "Outfit", "Plus Jakarta Sans", system-ui, sans-serif;
333
+    font-size: 0.92rem;
334
+    font-weight: 800;
335
+    letter-spacing: -0.02em;
336
+    color: var(--text-main);
196 337
   }
197 338
 
198
-  .saltacoda-totale-value {
339
+  .sc-preordine-empty {
199 340
     margin: 0;
200
-    color: var(--text-main);
201
-    font-weight: 800;
202
-    font-size: 1.05rem;
341
+    padding: 0.75rem 0.2rem;
342
+    font-size: 0.88rem;
343
+    color: var(--text-muted);
344
+    text-align: center;
203 345
   }
204 346
 
205
-  .saltacoda-codice-wrap {
206
-    text-align: center;
207
-    margin-bottom: 0.8rem;
347
+  .sc-preordine-actions {
348
+    display: grid;
349
+    gap: 0.55rem;
350
+    margin-top: 1rem;
351
+    padding-top: 0.25rem;
208 352
   }
209 353
 
210
-  .saltacoda-codice-label {
211
-    margin: 0;
212
-    font-size: 0.8rem;
213
-    color: var(--text-muted);
214
-    text-transform: uppercase;
215
-    letter-spacing: 0.02em;
354
+  .sc-preordine-actions .saltacoda-fest-btn {
355
+    width: 100%;
356
+    justify-content: center;
216 357
   }
217 358
 
218
-  .saltacoda-codice-value {
219
-    margin: 0.15rem 0 0;
220
-    font-size: 1.6rem;
221
-    line-height: 1.1;
359
+  .sc-modifica-avviso {
360
+    margin-bottom: 0.85rem;
361
+    padding: 0.9rem 1rem;
362
+    border-radius: 0.85rem;
363
+    background: color-mix(in srgb, #f59e0b 10%, #fff);
364
+    border: 1px solid color-mix(in srgb, #f59e0b 35%, transparent);
365
+  }
366
+
367
+  .sc-modifica-avviso__title {
368
+    margin: 0 0 0.35rem;
369
+    font-size: 0.88rem;
222 370
     font-weight: 800;
223
-    color: var(--card-accent);
371
+    color: color-mix(in srgb, #b45309 85%, var(--text-main));
224 372
   }
225 373
 
226
-  .saltacoda-codice-date {
227
-    margin: 0.25rem 0 0;
228
-    font-size: 0.85rem;
374
+  .sc-modifica-avviso__text,
375
+  .sc-modifica-avviso__list {
376
+    margin: 0;
377
+    font-size: 0.82rem;
378
+    line-height: 1.45;
229 379
     color: var(--text-muted);
230 380
   }
231 381
 
232
-  .saltacoda-stato-badge {
233
-    display: inline-block;
234
-    margin-top: 0.35rem;
235
-    padding: 0.2rem 0.65rem;
236
-    border-radius: 999px;
237
-    font-size: 0.75rem;
238
-    font-weight: 700;
239
-    text-transform: uppercase;
240
-    letter-spacing: 0.03em;
241
-    color: #fff;
242
-    background: linear-gradient(
243
-      135deg,
244
-      color-mix(in srgb, var(--card-accent) 92%, #fff) 0%,
245
-      color-mix(in srgb, var(--card-accent) 78%, #15265c) 100%
246
-    );
382
+  .sc-modifica-avviso__list {
383
+    margin-top: 0.45rem;
384
+    padding-left: 1.1rem;
247 385
   }
248 386
 
249
-  .saltacoda-preordine-content > .sc-istruzione-panel {
250
-    margin-bottom: 0.85rem;
387
+  .sc-preordine-actions__secondary {
388
+    display: flex;
389
+    justify-content: center;
390
+    margin-top: 0.15rem;
391
+  }
392
+
393
+  .sc-preordine-link {
394
+    border: 0;
395
+    background: transparent;
396
+    padding: 0.2rem 0;
397
+    font-size: 0.84rem;
398
+    font-weight: 700;
399
+    font-family: inherit;
400
+    color: var(--text-muted);
401
+    text-decoration: underline;
402
+    text-underline-offset: 0.15rem;
403
+    cursor: pointer;
251 404
   }
252 405
 
253 406
   @media (min-width: 768px) {
254
-    .saltacoda-preordine {
407
+    .sc-preordine-page {
255 408
       margin: 0 auto;
256 409
       max-width: 680px;
257 410
       border-left: 1px solid color-mix(in srgb, var(--text-main) 8%, transparent);
@@ -262,118 +415,177 @@
262 415
 @endsection
263 416
 
264 417
 @section('content')
265
-@php
266
-  $infoCliente = $saltacoda_ordine->info;
267
-  if (is_string($infoCliente)) {
268
-      $decodedInfo = json_decode($infoCliente, true);
269
-      $infoCliente = is_array($decodedInfo) ? $decodedInfo : [];
270
-  }
271
-@endphp
272
-
273 418
 <div class="saltacoda-cliente">
274 419
   <div class="saltacoda-cliente__bg" aria-hidden="true"></div>
275 420
 
276
-  <div class="saltacoda-preordine">
277
-    <header class="saltacoda-preordine-topbar saltacoda-fest-topbar">
278
-      <a href="{{ route('cliente.saltacoda.show', ['attivita_id' => $saltacoda_ordine->attivita_id]) }}" class="saltacoda-fest-topbar__back" aria-label="Torna al menu">
421
+  <div class="sc-preordine-page">
422
+    <header class="sc-preordine-topbar saltacoda-fest-topbar">
423
+      <a href="{{ $menuUrl }}" class="saltacoda-fest-topbar__back" aria-label="Torna al menu">
279 424
         <i class="bx bx-arrow-back"></i>
280 425
       </a>
281
-      <h1 class="saltacoda-fest-topbar__title">
282
-        Pre-ordine #{{ $saltacoda_ordine->codice ?? '—' }}
283
-      </h1>
284
-      <a
285
-        href="{{ route('cliente.saltacoda.show', ['attivita_id' => $saltacoda_ordine->attivita_id]) }}"
286
-        class="saltacoda-fest-btn saltacoda-fest-btn--sm"
287
-        target="_blank"
288
-        rel="noopener noreferrer">
289
-        <i class="bx bx-plus"></i> Nuovo
290
-      </a>
426
+      <div class="saltacoda-topbar-heading">
427
+        <h1 class="saltacoda-topbar-title">Ordine inviato</h1>
428
+        <p class="saltacoda-topbar-subtitle">{{ $attivita?->nome ?? 'Saltacoda' }}</p>
429
+      </div>
291 430
       <a href="{{ $backUrl }}" class="saltacoda-fest-topbar__brand" aria-label="{{ config('app.name') }}">
292 431
         <img src="{{ $festLogoLarge }}" alt="{{ config('app.name') }}" loading="lazy">
293 432
       </a>
294 433
     </header>
295 434
 
296
-    <main class="saltacoda-preordine-content">
435
+    <main class="sc-preordine-content">
297 436
       @include('saltacoda._partials.cliente.istruzione-panel', [
298 437
         'slotData' => $istruzioni['post_invio'] ?? Saltacoda::defaultSlot(),
299 438
         'ariaLabel' => 'Messaggio post-invio',
300 439
       ])
301 440
 
302
-      <section class="saltacoda-section">
303
-        <h2 class="saltacoda-section-title">Riepilogo</h2>
304
-        <div class="saltacoda-codice-wrap">
305
-          <p class="saltacoda-codice-label">Codice pre-ordine</p>
306
-          <p class="saltacoda-codice-value">#{{ $saltacoda_ordine->codice ?? '—' }}</p>
307
-          <span class="saltacoda-stato-badge">{{ $saltacoda_ordine->stato ?? '—' }}</span>
308
-          <p class="saltacoda-codice-date">
309
-            {{ $saltacoda_ordine->created_at ? $saltacoda_ordine->created_at->format('d/m/Y H:i') : '—' }}
441
+      <section class="sc-preordine-hero" aria-labelledby="scPreordineHeroTitle">
442
+        <div class="sc-preordine-hero__mark" aria-hidden="true">
443
+          <i class="bx bx-check"></i>
444
+        </div>
445
+        <h2 class="sc-preordine-hero__title" id="scPreordineHeroTitle">Grazie!</h2>
446
+        <p class="sc-preordine-hero__hint">Il tuo pre-ordine è stato registrato.</p>
447
+        <div class="sc-preordine-code-card" aria-label="Codice pre-ordine">
448
+          <p class="sc-preordine-code-card__label">Conserva il codice.</p>
449
+          <p class="sc-preordine-code-card__value">
450
+            <!-- {{ $saltacoda_ordine->codice ?? '—' }} -->
451
+            {{ \App\Models\SaltacodaOrdine::formattaCodice($saltacoda_ordine->codice) }}
310 452
           </p>
311 453
         </div>
454
+        <div class="sc-preordine-meta">
455
+          @if(filled($saltacoda_ordine->stato))
456
+            <span class="sc-preordine-stato">{{ $saltacoda_ordine->stato }}</span>
457
+          @endif
458
+          @if($saltacoda_ordine->created_at)
459
+            <span class="sc-preordine-meta__item">{{ $saltacoda_ordine->created_at->format('d/m/Y · H:i') }}</span>
460
+          @endif
461
+        </div>
312 462
       </section>
313 463
 
314
-      <section class="saltacoda-section">
315
-        <h2 class="saltacoda-section-title">Dettaglio ordine</h2>
316
-        @php
317
-          $righeOrdine = $saltacoda_ordine->saltacoda_riga_ordine ?? $saltacoda_ordine->riga_ordine ?? collect();
318
-        @endphp
464
+      <section class="sc-preordine-block" aria-labelledby="scPreordineItemsTitle">
465
+        <h3 class="sc-preordine-block__title" id="scPreordineItemsTitle">Cosa hai ordinato</h3>
319 466
         @if($righeOrdine->count())
320
-          @foreach($righeOrdine as $riga)
321
-            <article class="saltacoda-riga">
322
-              <div class="saltacoda-riga-head">
323
-                <p class="saltacoda-riga-name mb-0">{{ $riga->piatto->nome ?? 'Piatto #' . $riga->piatto_id }}</p>
324
-                <span class="saltacoda-riga-prezzo">&euro; {{ number_format((float) $riga->prezzo, 2, ',', '.') }}</span>
325
-              </div>
326
-              @if($riga->saltacoda_has_variante && $riga->saltacoda_has_variante->count())
327
-                <div class="saltacoda-riga-varianti">
328
-                  @foreach($riga->saltacoda_has_variante as $rigaVariante)
329
-                    @php
330
-                      $variante = $rigaVariante->variante_piatto;
331
-                      $extra = (float) ($variante->prezzo ?? 0);
332
-                    @endphp
333
-                    <p class="saltacoda-riga-variante mb-0">
334
-                      {{ $variante->label ?? 'Variante' }}
335
-                      @if($extra > 0)
336
-                        <span>+&euro; {{ number_format($extra, 2, ',', '.') }}</span>
467
+          <div class="sc-checkout-cart">
468
+            <div class="sc-checkout-cart__head">
469
+              <span class="sc-checkout-cart__label">Riepilogo ordine</span>
470
+              <span class="sc-checkout-cart__count">{{ $countLabel }}</span>
471
+            </div>
472
+            <div class="sc-checkout-cart__list">
473
+              @foreach($righeOrdine as $riga)
474
+                @php
475
+                  $nomePiatto = $riga->piatto->nome ?? ('Piatto #' . $riga->piatto_id);
476
+                  $quantita = (int) ($riga->quantita ?? 1);
477
+                  $prezzoRiga = (float) ($riga->prezzo ?? 0);
478
+                @endphp
479
+                <div class="sc-checkout-cart-line">
480
+                  <div class="sc-checkout-cart-line__main">
481
+                    <span class="sc-checkout-cart-line__qty">{{ $quantita }}×</span>
482
+                    <div class="sc-checkout-cart-line__copy">
483
+                      <span class="sc-checkout-cart-line__name">{{ $nomePiatto }}</span>
484
+                      @if($riga->saltacoda_has_variante && $riga->saltacoda_has_variante->count())
485
+                        <div class="sc-checkout-cart-line__varianti">
486
+                          @foreach($riga->saltacoda_has_variante as $rigaVariante)
487
+                            @php
488
+                              $variante = $rigaVariante->variante_piatto;
489
+                              if (!$variante) {
490
+                                  continue;
491
+                              }
492
+                              $scopeClass = filled($variante->piatto_id)
493
+                                ? 'sc-checkout-variant--piatto'
494
+                                : 'sc-checkout-variant--cucina';
495
+                              $extra = (float) ($variante->prezzo ?? 0);
496
+                            @endphp
497
+                            <span class="sc-checkout-variant {{ $scopeClass }}">
498
+                              {{ $variante->label ?? 'Variante' }}
499
+                              @if($extra > 0)
500
+                                <span class="sc-checkout-variant__extra">+€ {{ number_format($extra, 2, ',', '.') }}</span>
501
+                              @endif
502
+                            </span>
503
+                          @endforeach
504
+                        </div>
505
+                      @endif
506
+                      @if(filled($riga->note))
507
+                        <p class="sc-checkout-cart-line__note">Nota: {{ $riga->note }}</p>
337 508
                       @endif
338
-                    </p>
339
-                  @endforeach
509
+                    </div>
510
+                  </div>
511
+                  <span class="sc-checkout-cart-line__total">€ {{ number_format($prezzoRiga, 2, ',', '.') }}</span>
340 512
                 </div>
341
-              @endif
342
-              <div class="saltacoda-riga-meta">
343
-                <p class="saltacoda-riga-note mb-0">{{ $riga->note ?: 'Nessuna nota' }}</p>
344
-                <span class="saltacoda-riga-qta">x{{ $riga->quantita }}</span>
345
-              </div>
346
-            </article>
347
-          @endforeach
348
-          <div class="saltacoda-totali">
349
-            <div>
350
-              <p class="saltacoda-totale-label">Totale articoli</p>
351
-              <p class="saltacoda-info-value mb-0">{{ $righeOrdine->sum('quantita') }}</p>
513
+              @endforeach
352 514
             </div>
353
-            <div class="text-end">
354
-              <p class="saltacoda-totale-label">Totale ordine</p>
355
-              <p class="saltacoda-totale-value">&euro; {{ number_format((float) $saltacoda_ordine->prezzo, 2, ',', '.') }}</p>
515
+            <div class="sc-checkout-cart__foot">
516
+              <span>Totale</span>
517
+              <strong>€ {{ number_format((float) $saltacoda_ordine->prezzo, 2, ',', '.') }}</strong>
356 518
             </div>
357 519
           </div>
358 520
         @else
359
-          <p class="text-muted mb-0">Nessun piatto presente nell'ordine.</p>
521
+          <p class="sc-preordine-empty">Nessun piatto presente nell'ordine.</p>
360 522
         @endif
361 523
       </section>
362 524
 
363
-      @if(!empty($infoCliente))
364
-        <section class="saltacoda-section">
365
-          <h2 class="saltacoda-section-title">Informazioni cliente</h2>
366
-          <div class="saltacoda-info-list">
367
-            @foreach($infoCliente as $key => $val)
368
-              <div class="saltacoda-info-row">
369
-                <p class="saltacoda-info-key mb-0">{{ ucfirst(str_replace('_', ' ', $key)) }}</p>
370
-                <p class="saltacoda-info-value mb-0">{{ $val ?: '—' }}</p>
371
-              </div>
372
-            @endforeach
525
+      @if(!empty($infoClienteRows))
526
+        <section class="sc-preordine-block" aria-labelledby="scPreordineInfoTitle">
527
+          <h3 class="sc-preordine-block__title" id="scPreordineInfoTitle">I tuoi dati</h3>
528
+          <div class="sc-checkout-info">
529
+            <div class="sc-checkout-info__list">
530
+              @foreach($infoClienteRows as $row)
531
+                <div class="sc-checkout-info-row">
532
+                  <span class="sc-checkout-info-row__label">{{ $row['label'] }}</span>
533
+                  <span class="sc-checkout-info-row__value">{{ $row['value'] }}</span>
534
+                </div>
535
+              @endforeach
536
+            </div>
373 537
           </div>
374 538
         </section>
375 539
       @endif
540
+
541
+      <div class="sc-preordine-actions">
542
+        @if($modificaUrl)
543
+          <a href="{{ $modificaUrl }}" class="saltacoda-fest-btn" id="scModificaOrdineBtn">
544
+            <i class="bx bx-edit"></i> Modifica ordine
545
+          </a>
546
+        @endif
547
+        <a href="{{ $menuUrl }}" class="saltacoda-fest-btn">
548
+          <i class="bx bx-plus"></i> Nuovo ordine
549
+        </a>
550
+        <div class="sc-preordine-actions__secondary">
551
+          <a href="{{ $backUrl }}" class="sc-preordine-link">Torna all'attività</a>
552
+        </div>
553
+      </div>
376 554
     </main>
377 555
   </div>
378 556
 </div>
557
+
558
+@if($modificaUrl)
559
+  <script>
560
+    (function () {
561
+      const modificaBtn = document.getElementById('scModificaOrdineBtn');
562
+      const avvisiModifica = @json($avvisi_modifica);
563
+      if (!modificaBtn) {
564
+        return;
565
+      }
566
+
567
+      modificaBtn.addEventListener('click', function (event) {
568
+        event.preventDefault();
569
+        const righe = [
570
+          'Il menu potrebbe essere cambiato. Piatti o opzioni non più disponibili verranno rimossi quando salvi le modifiche.',
571
+        ];
572
+
573
+        if (avvisiModifica && avvisiModifica.ha_problemi) {
574
+          (avvisiModifica.piatti_rimossi || []).forEach((piatto) => {
575
+            righe.push(`• ${piatto.qta}× ${piatto.nome} (non più disponibile)`);
576
+          });
577
+          (avvisiModifica.varianti_rimosse || []).forEach((variante) => {
578
+            righe.push(`• ${variante.label} su ${variante.piatto} (non più disponibile)`);
579
+          });
580
+        }
581
+
582
+        righe.push('', 'Vuoi continuare?');
583
+
584
+        if (window.confirm(righe.join('\n'))) {
585
+          window.location.href = modificaBtn.href;
586
+        }
587
+      });
588
+    })();
589
+  </script>
590
+@endif
379 591
 @endsection

+ 1610
- 548
resources/views/saltacoda/show.blade.php
File diff soppresso perché troppo grande
Vedi File


Loading…
Annulla
Salva