gruene-seele/resources/views/admin/product/index.blade.php
Kevin Adametz 9ed47c02f3 Warenwirtschaft: OPT-9 AuthZ/CSRF-Härtung (Audit M-2, M-3, M-6, M-7, M-10)
- M-2: Produktionen buchen Bestand — StoreProductionRequest verlangt
  jetzt admin >= 7 (wie manuelle Bestandsbewegungen); create/edit/copy
  im Controller gegated, UI-Buttons für niedrigere Stufen ausgeblendet.
- M-3: Ausschuss-/Bewegungs-/Produktions-Buttons nur noch für
  Berechtigte sichtbar (keine toten Formulare mehr).
- M-6: Zustandsändernde GET-Routen umgestellt — stock-entries/copy
  jetzt POST mit CSRF; /admin/product/delete jetzt DELETE-Formulare
  (Produktliste + Rezeptur-Zeile im Produktformular).
- M-7: ProductController::imageAttribute whitelistet Spalten
  ('active', 'pos') + numerische Werte, sonst 400 — kein beliebiger
  Spalten-Write per URL mehr.
- M-10: URL-Felder (Lieferant, Verpackungsartikel, INCI-Lieferanten-
  Repeater) validieren url:http,https — keine speicherbaren
  javascript:-Links mehr.

Tests: ProductionHardeningTest +2; drei bestehende Tests an neue
Regeln angepasst (admin-Level 7 für Produktions-Formular, POST-Copy).
Suite: 231 grün.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 12:55:11 +00:00

137 lines
7.3 KiB
PHP
Executable file

@extends('layouts.layout-2')
@section('content')
<div class="card">
<h6 class="card-header">
{{ __('Produkte') }}
<label class="custom-control custom-checkbox float-right mb-0">
<input type="checkbox" class="custom-control-input show-active-products" name="show_active_products"
@if (get_user_attr('show_active_products') === 'true') checked @endif>
<span class="custom-control-label font-style-normal font-weight-normal">nur aktive anzeigen</span>
</label>
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-product table table-striped table-bordered">
<thead>
<tr>
<th style="max-width: 60px;">&nbsp;</th>
<th>{{ __('Pos') }}</th>
<th>{{ __('Bild') }}</th>
<th>{{ __('Name') }}</th>
<th>{{ __('Artikelnummer') }}</th>
<th>{{ __('Kategorie') }}</th>
<th>{{ __('Preis') }}</th>
<th>{{ __('Inhalt') }}</th>
<th>{{ __('Einheit') }}</th>
<th>{{ __('Grundpreis') }}</th>
<th>{{ __('Gewicht') }}</th>
<th>{{ __('Verfügbarkeit') }}</th>
<th>{{ __('sichbar') }}</th>
<th>
<div data-toggle="tooltip" title data-original-title="White Label">{{ __('WL') }}</div>
</th>
<th>
<div data-toggle="tooltip" title data-original-title="Kompensationsprodukt">{{ __('KP') }}
</div>
</th>
<th>
<div data-toggle="tooltip" title data-original-title="Maximaler Kauf pro Berater">
{{ __('MK') }}</div>
</th>
<th>
<div data-toggle="tooltip" title data-original-title="Einzelrabatt">{{ __('ER') }}</div>
</th>
<th>
<div data-toggle="tooltip" title data-original-title="Auswertung Absatzmengen ausschließen ">
{{ __('AA') }}</div>
</th>
<th>{{ __('Status') }}</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach ($values as $value)
<tr>
<td>
<a href="{{ route('admin_product_edit', [$value->id]) }}"
class="btn icon-btn btn-sm btn-primary">
<span class="far fa-edit"></span>
</a>
</td>
<td>{{ $value->pos }}</td>
<td>
@if (count($value->images))
<img class="img-fluid" alt="" style="max-height: 80px"
src="{{ route('product_image', [$value->images->first()->slug]) }}">
@endif
</td>
<td>{{ $value->name }}</td>
<td>{{ $value->number }}</td>
<td>
@foreach ($value->categories as $category)
<div style="white-space: nowrap">{{ $category->category->name }}</div>
@endforeach
</td>
<td>{{ $value->getFormattedPrice() }}</td>
<td>{{ $value->contents_total }}</td>
<td>{{ $value->getUnitType() }}</td>
<td>{{ $value->getBasePriceFormatted() }}</td>
<td>{{ $value->weight }}</td>
<td data-sort="{{ $value->isOutOfStock() ? 1 : 0 }}">
@if ($value->isOutOfStock())
<span class="badge badge-danger"
style="white-space: normal;">{{ $value->outOfStockNotice() }}</span>
@else
<span class="text-muted small">{{ __('vorrätig') }}</span>
@endif
</td>
<td>{!! $value->getShowOnTypes('<br>') !!}</td>
<td data-sort="{{ $value->whitelabel }}">{!! get_active_badge($value->whitelabel, $value->whitelabel_name) !!}</td>
<td data-sort="{{ $value->shipping_addon }}">{!! get_active_badge($value->shipping_addon) !!}</td>
<td data-sort="{{ $value->max_buy }}">{!! get_active_badge($value->max_buy) !!}</td>
<td data-sort="{{ $value->single_commission }}">{!! get_active_badge($value->single_commission) !!}</td>
<td data-sort="{{ $value->exclude_stats_sales }}">{!! get_active_badge($value->exclude_stats_sales) !!}</td>
<td data-sort="{{ $value->active }}">{!! get_active_badge($value->active) !!}</td>
<td><a class="text-info" href="{{ route('admin_product_copy', [$value->id]) }}"
onclick="return confirm('{{ __('Eintrag kopieren?') }}');"><i
class="far fa-copy"></i></a> &nbsp;
<form method="post" action="{{ route('admin_product_delete', [$value->id]) }}" class="d-inline"
onsubmit="return confirm('{{ __('Really delete entry?') }}');">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-link text-danger p-0 border-0 align-baseline"><i class="far fa-trash-alt"></i></button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
<div class="mt-4 ml-4">
<a href="{{ route('admin_product_edit', ['new']) }}" class="btn btn-sm btn-primary">
{{ __('Neues Produkt erstellen') }}
</a>
</div>
</div>
</div>
<script>
$(document).ready(function() {
$('.datatables-product').dataTable({
"bLengthChange": false,
"iDisplayLength": 50,
"language": {
"url": "/js/German.json"
}
});
$('.show-active-products').on('change', function() {
console.log($(this));
console.log($(this).prop('checked'));
document.location.search = $(this).attr('name') + "=" + $(this).prop('checked');
// window.location.reload(true);
})
});
</script>
@endsection