Warenwirtschaft: einheitliches UI-Design (AP-19)

- Zentrale, wiederverwendbare Design-Partial wawi-ui.blade.php (gescoptes Inline-CSS, kein Build noetig)
- Bausteine: Seitenkopf, Kennzahlen-Kacheln, Karten, Toolbar/Suche, aufgeraeumte Tabellen, Status-Pills, Datenblatt-Definitionsliste, Name-Zelle mit fester Icon-Spalte, Leer-Zustaende
- Umgestellt: alle Uebersicht-/Listen- und Detailseiten unter admin/inventory
- Responsive: Detail-Datenblaetter brechen unter 768px gestapelt um (Label oben, Wert linksbuendig); Icon-Spalte verhindert Versatz bei Zeilenumbruch
- Entwicklungsplan um AP-19 + UI-Konvention ergaenzt

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Kevin Adametz 2026-06-03 13:09:55 +00:00
parent 3ee2d756e9
commit a8f6fef38e
21 changed files with 1609 additions and 955 deletions

View file

@ -1,86 +1,97 @@
@extends('layouts.layout-2')
@section('content')
@include('admin.inventory.partials.table-actions-style')
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Wareneingang') }}</span>
@if(Auth::user()->isAdmin())
<a href="{{ route('admin.inventory.stock-entries.create') }}" class="btn btn-sm btn-primary">{{ __('Neuer Einkauf') }}</a>
@endif
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-style table table-striped table-bordered wawi-table">
<thead>
<tr>
<th style="max-width: 90px;"></th>
<th>{{ __('Status') }}</th>
<th>{{ __('Bestellt') }}</th>
<th>{{ __('Art') }}</th>
<th>{{ __('Artikel') }}</th>
<th>{{ __('Lieferant') }}</th>
<th>{{ __('Menge') }}</th>
<th style="max-width: 60px;"></th>
</tr>
</thead>
<tbody>
@foreach($values as $row)
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ __('Einkauf & Wareneingang') }}</h1>
<p class="wawi-page-head__subtitle">{{ __('Bestellungen und Wareneingänge von Rohstoffen und Verpackung') }}</p>
</div>
<div class="wawi-page-head__actions">
@if(Auth::user()->isAdmin())
<a href="{{ route('admin.inventory.stock-entries.create') }}" class="btn btn-primary btn-sm">
<span class="fas fa-plus mr-1"></span>{{ __('Neuer Einkauf') }}
</a>
@endif
</div>
</div>
<div class="wawi-card">
<div class="card-datatable table-responsive p-2">
<table class="datatables-style table wawi-table">
<thead>
<tr>
<td class="text-nowrap">
<a href="{{ route('admin.inventory.stock-entries.show', $row) }}" class="btn icon-btn btn-sm btn-primary" title="{{ __('Ansicht') }}">
<span class="far fa-eye"></span>
</a>
@if(Auth::user()->isAdmin() && $row->status === 'pending')
<a href="{{ route('admin.inventory.stock-entries.edit', $row) }}" class="btn icon-btn btn-sm btn-secondary" title="{{ __('Bearbeiten') }}">
<span class="far fa-edit"></span>
</a>
@endif
@if(Auth::user()->isAdmin())
<a href="{{ route('admin.inventory.stock-entries.copy', $row) }}" class="btn icon-btn btn-sm btn-info" title="{{ __('Duplizieren') }}">
<span class="far fa-copy"></span>
</a>
@endif
</td>
<td data-sort="{{ $row->status === 'pending' ? 0 : 1 }}">
@if($row->status === 'pending')
<span class="badge badge-warning">{{ __('Offen') }}</span>
@else
<span class="badge badge-success">{{ __('Eingegangen') }}</span>
@endif
</td>
<td data-sort="{{ $row->ordered_at?->timestamp ?? 0 }}">{{ $row->ordered_at?->format('d.m.Y') }}</td>
<td>{{ $entryTypeLabels[$row->entry_type] ?? $row->entry_type }}</td>
<td>
@if($row->entry_type === 'ingredient' && $row->ingredient)
{{ $row->ingredient->name }}
@elseif($row->packagingItem)
{{ $row->packagingItem->name }}
@else
@endif
</td>
<td>{{ $row->supplier?->name ?? '—' }}</td>
<td>
@if($row->unit === 'gram')
{{ \App\Services\Util::formatNumber($row->ordered_quantity) }} g
@else
{{ \App\Services\Util::formatNumber($row->ordered_quantity, 0) }} {{ __('Stk.') }}
@endif
</td>
<td>
@if(Auth::user()->isAdmin() && $row->status === 'pending')
<form action="{{ route('admin.inventory.stock-entries.destroy', $row) }}" method="post" class="d-inline"
onsubmit="return confirm(@json(__('Eintrag wirklich löschen?')));">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-link text-danger p-0" title="{{ __('Löschen') }}"><i class="far fa-trash-alt"></i></button>
</form>
@endif
</td>
<th style="max-width: 90px;"></th>
<th>{{ __('Status') }}</th>
<th>{{ __('Bestellt') }}</th>
<th>{{ __('Art') }}</th>
<th>{{ __('Artikel') }}</th>
<th>{{ __('Lieferant') }}</th>
<th>{{ __('Menge') }}</th>
<th style="max-width: 60px;"></th>
</tr>
@endforeach
</tbody>
</table>
</thead>
<tbody>
@foreach($values as $row)
<tr>
<td class="text-nowrap">
<a href="{{ route('admin.inventory.stock-entries.show', $row) }}" class="btn icon-btn btn-sm btn-primary" title="{{ __('Ansicht') }}">
<span class="far fa-eye"></span>
</a>
@if(Auth::user()->isAdmin() && $row->status === 'pending')
<a href="{{ route('admin.inventory.stock-entries.edit', $row) }}" class="btn icon-btn btn-sm btn-secondary" title="{{ __('Bearbeiten') }}">
<span class="far fa-edit"></span>
</a>
@endif
@if(Auth::user()->isAdmin())
<a href="{{ route('admin.inventory.stock-entries.copy', $row) }}" class="btn icon-btn btn-sm btn-info" title="{{ __('Duplizieren') }}">
<span class="far fa-copy"></span>
</a>
@endif
</td>
<td data-sort="{{ $row->status === 'pending' ? 0 : 1 }}">
@if($row->status === 'pending')
<span class="wawi-pill wawi-pill--warning">{{ __('Offen') }}</span>
@else
<span class="wawi-pill wawi-pill--ok">{{ __('Eingegangen') }}</span>
@endif
</td>
<td data-sort="{{ $row->ordered_at?->timestamp ?? 0 }}">{{ $row->ordered_at?->format('d.m.Y') }}</td>
<td>{{ $entryTypeLabels[$row->entry_type] ?? $row->entry_type }}</td>
<td>
@if($row->entry_type === 'ingredient' && $row->ingredient)
<span class="wawi-item-name">{{ $row->ingredient->name }}</span>
@elseif($row->packagingItem)
<span class="wawi-item-name">{{ $row->packagingItem->name }}</span>
@else
@endif
</td>
<td>{{ $row->supplier?->name ?? '—' }}</td>
<td>
@if($row->unit === 'gram')
{{ \App\Services\Util::formatNumber($row->ordered_quantity) }} g
@else
{{ \App\Services\Util::formatNumber($row->ordered_quantity, 0) }} {{ __('Stk.') }}
@endif
</td>
<td>
@if(Auth::user()->isAdmin() && $row->status === 'pending')
<form action="{{ route('admin.inventory.stock-entries.destroy', $row) }}" method="post" class="d-inline"
onsubmit="return confirm(@json(__('Eintrag wirklich löschen?')));">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-link text-danger p-0" title="{{ __('Löschen') }}"><i class="far fa-trash-alt"></i></button>
</form>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
<script>

View file

@ -5,18 +5,21 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">{{ __('Wareneingang') }}</h4>
<div class="card mb-3">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>
@if($model->status === 'pending')
<span class="badge badge-warning">{{ __('Offen') }}</span>
@else
<span class="badge badge-success">{{ __('Eingegangen') }}</span>
@endif
</span>
<span>
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">
{{ __('Wareneingang') }}
@if($model->status === 'pending')
<span class="wawi-pill wawi-pill--warning">{{ __('Offen') }}</span>
@else
<span class="wawi-pill wawi-pill--ok">{{ __('Eingegangen') }}</span>
@endif
</h1>
<p class="wawi-page-head__subtitle">{{ $entryTypeLabels[$model->entry_type] ?? $model->entry_type }}</p>
</div>
<div class="wawi-page-head__actions">
<a href="{{ route('admin.inventory.stock-entries.index') }}" class="btn btn-sm btn-outline-secondary">{{ __('Zurück zur Liste') }}</a>
@if(Auth::user()->isAdmin())
<a href="{{ route('admin.inventory.stock-entries.copy', $model) }}" class="btn btn-sm btn-outline-info">{{ __('Duplizieren') }}</a>
@ -24,112 +27,145 @@
@if(Auth::user()->isAdmin() && $model->isPending())
<a href="{{ route('admin.inventory.stock-entries.edit', $model) }}" class="btn btn-sm btn-primary">{{ __('Bearbeiten') }}</a>
@endif
</span>
</h6>
<div class="card-body">
<dl class="row mb-0">
<dt class="col-sm-3">{{ __('Art') }}</dt>
<dd class="col-sm-9">{{ $entryTypeLabels[$model->entry_type] ?? $model->entry_type }}</dd>
</div>
</div>
<dt class="col-sm-3">{{ __('Artikel') }}</dt>
<dd class="col-sm-9">
@if($model->entry_type === 'ingredient' && $model->ingredient)
{{ $model->ingredient->name }}@if($model->ingredient->inci) ({{ $model->ingredient->inci }})@endif
@elseif($model->packagingItem)
{{ $model->packagingItem->name }}
@if($model->packagingItem->packagingMaterial)
<span class="text-muted"> {{ $model->packagingItem->packagingMaterial->name }}</span>
<div class="card wawi-card mb-3">
<div class="wawi-card__header">{{ __('Details') }}</div>
<div class="card-body">
<div class="wawi-deflist">
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Art') }}</span>
<span class="wawi-deflist__value">{{ $entryTypeLabels[$model->entry_type] ?? $model->entry_type }}</span>
</div>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Artikel') }}</span>
<span class="wawi-deflist__value">
@if($model->entry_type === 'ingredient' && $model->ingredient)
{{ $model->ingredient->name }}@if($model->ingredient->inci) ({{ $model->ingredient->inci }})@endif
@elseif($model->packagingItem)
{{ $model->packagingItem->name }}
@if($model->packagingItem->packagingMaterial)
<span class="text-muted"> {{ $model->packagingItem->packagingMaterial->name }}</span>
@endif
@else
@endif
@else
@endif
</dd>
</span>
</div>
@if($model->entry_type === 'ingredient' && $model->quality)
<dt class="col-sm-3">{{ __('Rohstoffqualität') }}</dt>
<dd class="col-sm-9">{{ $model->quality->name }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Rohstoffqualität') }}</span>
<span class="wawi-deflist__value">{{ $model->quality->name }}</span>
</div>
@endif
<dt class="col-sm-3">{{ __('Lieferant') }}</dt>
<dd class="col-sm-9">{{ $model->supplier?->name ?? '—' }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Lieferant') }}</span>
<span class="wawi-deflist__value">{{ $model->supplier?->name ?? '—' }}</span>
</div>
<dt class="col-sm-3">{{ __('Lagerort') }}</dt>
<dd class="col-sm-9">{{ $model->location?->name ?? '—' }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Lagerort') }}</span>
<span class="wawi-deflist__value">{{ $model->location?->name ?? '—' }}</span>
</div>
<dt class="col-sm-3">{{ __('Bestelldatum') }}</dt>
<dd class="col-sm-9">{{ $model->ordered_at?->format('d.m.Y') }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Bestelldatum') }}</span>
<span class="wawi-deflist__value">{{ $model->ordered_at?->format('d.m.Y') }}</span>
</div>
<dt class="col-sm-3">{{ __('Bestellte Menge') }}</dt>
<dd class="col-sm-9">
@if($model->unit === 'gram')
{{ \App\Services\Util::formatNumber($model->ordered_quantity) }} g
@else
{{ \App\Services\Util::formatNumber($model->ordered_quantity, 0) }} {{ __('Stk.') }}
@endif
</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Bestellte Menge') }}</span>
<span class="wawi-deflist__value">
@if($model->unit === 'gram')
{{ \App\Services\Util::formatNumber($model->ordered_quantity) }} g
@else
{{ \App\Services\Util::formatNumber($model->ordered_quantity, 0) }} {{ __('Stk.') }}
@endif
</span>
</div>
@if(Auth::user()->isAdmin())
<dt class="col-sm-3">{{ __('Preise (Stufe 1)') }}</dt>
<dd class="col-sm-9">
@if($model->entry_type === 'ingredient')
@if($model->price_per_kg !== null)
{{ \App\Services\Util::formatNumber($model->price_per_kg) }} / kg {{ __('netto') }}
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Preise (Stufe 1)') }}</span>
<span class="wawi-deflist__value">
@if($model->entry_type === 'ingredient')
@if($model->price_per_kg !== null)
{{ \App\Services\Util::formatNumber($model->price_per_kg) }} / kg {{ __('netto') }}
@else
@endif
@if($model->price_per_kg_gross !== null)
<span class="text-muted">· {{ \App\Services\Util::formatNumber($model->price_per_kg_gross) }} / kg {{ __('brutto') }}</span>
@endif
@if($model->tax_rate_percent !== null)
<span class="text-muted">· {{ __('USt.') }} {{ \App\Services\Util::formatNumber($model->tax_rate_percent) }} %</span>
@endif
@else
@if($model->price_total !== null)
{{ \App\Services\Util::formatNumber($model->price_total) }} {{ __('netto') }}
@else
@endif
@endif
@if($model->price_per_kg_gross !== null)
<span class="text-muted">· {{ \App\Services\Util::formatNumber($model->price_per_kg_gross) }} / kg {{ __('brutto') }}</span>
@endif
@if($model->tax_rate_percent !== null)
<span class="text-muted">· {{ __('USt.') }} {{ \App\Services\Util::formatNumber($model->tax_rate_percent) }} %</span>
@endif
@else
@if($model->price_total !== null)
{{ \App\Services\Util::formatNumber($model->price_total) }} {{ __('netto') }}
@else
@endif
@endif
</dd>
</span>
</div>
@endif
<dt class="col-sm-3">{{ __('Bestellt von') }}</dt>
<dd class="col-sm-9">{{ $model->orderedByUser?->getFullName(false) ?: $model->orderedByUser?->email ?? '—' }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Bestellt von') }}</span>
<span class="wawi-deflist__value">{{ $model->orderedByUser?->getFullName(false) ?: $model->orderedByUser?->email ?? '—' }}</span>
</div>
@if($model->isReceived())
<dt class="col-sm-3">{{ __('Eingangsdatum') }}</dt>
<dd class="col-sm-9">{{ $model->received_at?->format('d.m.Y') }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Eingangsdatum') }}</span>
<span class="wawi-deflist__value">{{ $model->received_at?->format('d.m.Y') }}</span>
</div>
<dt class="col-sm-3">{{ __('Eingegangene Menge') }}</dt>
<dd class="col-sm-9">
@if($model->unit === 'gram')
{{ \App\Services\Util::formatNumber($model->received_quantity) }} g
@else
{{ \App\Services\Util::formatNumber($model->received_quantity, 0) }} {{ __('Stk.') }}
@endif
</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Eingegangene Menge') }}</span>
<span class="wawi-deflist__value">
@if($model->unit === 'gram')
{{ \App\Services\Util::formatNumber($model->received_quantity) }} g
@else
{{ \App\Services\Util::formatNumber($model->received_quantity, 0) }} {{ __('Stk.') }}
@endif
</span>
</div>
@if($model->entry_type === 'ingredient')
<dt class="col-sm-3">{{ __('Charge') }}</dt>
<dd class="col-sm-9">{{ $model->batch_number ?: '—' }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Charge') }}</span>
<span class="wawi-deflist__value">{{ $model->batch_number ?: '—' }}</span>
</div>
<dt class="col-sm-3">{{ __('Mindesthaltbarkeit') }}</dt>
<dd class="col-sm-9">{{ $model->best_before?->format('d.m.Y') ?? '—' }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Mindesthaltbarkeit') }}</span>
<span class="wawi-deflist__value">{{ $model->best_before?->format('d.m.Y') ?? '—' }}</span>
</div>
<dt class="col-sm-3">{{ __('Materialqualität') }}</dt>
<dd class="col-sm-9">{{ $model->quality?->name ?? '—' }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Materialqualität') }}</span>
<span class="wawi-deflist__value">{{ $model->quality?->name ?? '—' }}</span>
</div>
@endif
<dt class="col-sm-3">{{ __('Eingebucht von') }}</dt>
<dd class="col-sm-9">{{ $model->receivedByUser?->getFullName(false) ?: $model->receivedByUser?->email ?? '—' }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Eingebucht von') }}</span>
<span class="wawi-deflist__value">{{ $model->receivedByUser?->getFullName(false) ?: $model->receivedByUser?->email ?? '—' }}</span>
</div>
@endif
</dl>
</div>
</div>
</div>
@if($model->isPending())
<div class="card">
<h6 class="card-header">{{ __('Wareneingang buchen (Stufe 2)') }}</h6>
<div class="card wawi-card">
<div class="wawi-card__header">{{ __('Wareneingang buchen (Stufe 2)') }}</div>
<div class="card-body">
<form method="post" action="{{ route('admin.inventory.stock-entries.receive', $model) }}">
@csrf
@ -202,4 +238,5 @@
</div>
</div>
@endif
</div>
@endsection