Initial commit

This commit is contained in:
2026-09-03 12:21:17 +02:00
commit 6d40da0410
155 changed files with 21554 additions and 0 deletions
@@ -0,0 +1,47 @@
<x-app-layout :active="'metering.index'" title="SoftSPM 2 - Nowe urządzenie opomiarowania">
<div class="max-w-2xl space-y-stack-gap">
<div class="flex items-center gap-2">
<a href="{{ route('metering.index') }}" class="font-body-sm text-body-sm text-on-surface-variant hover:text-primary">← Wróć</a>
<h1 class="font-headline-lg text-headline-lg text-on-surface">Nowe urządzenie opomiarowania</h1>
</div>
@if ($errors->any())
<div class="status-chip-alert px-3 py-2 rounded-lg font-body-sm text-body-sm">{{ $errors->first() }}</div>
@endif
<form method="POST" action="{{ route('metering.store') }}" enctype="multipart/form-data"
class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card p-card-inner-padding space-y-4">
@csrf
<x-well-field name="name" label="Nazwa" :value="old('name')" required />
<x-well-select name="manufacturer_id" label="Producent" :options="$manufacturers" :selected="old('manufacturer_id')" required placeholder="- wybierz -" />
<div class="space-y-1">
<label for="description" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Opis</label>
<textarea id="description" name="description" rows="3"
class="w-full bg-surface-container-low border border-outline-variant px-3 py-2 rounded focus:ring-1 focus:ring-primary focus:border-primary font-body-md text-on-surface">{{ old('description') }}</textarea>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div class="space-y-1">
<label for="catalogue_file" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Dokumentacja katalogowa</label>
<input id="catalogue_file" name="catalogue_file" type="file" accept=".pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx"
class="w-full font-body-sm text-body-sm text-on-surface file:mr-3 file:px-4 file:py-2 file:rounded-full file:border-0 file:bg-secondary file:text-on-secondary file:font-title-sm file:cursor-pointer hover:file:opacity-90">
<p class="font-body-sm text-body-sm text-on-surface-variant/70">PDF lub Office, do 8 MB.</p>
</div>
<div class="space-y-1">
<label for="image_file" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Zdjęcie</label>
<input id="image_file" name="image_file" type="file" accept="image/*"
class="w-full font-body-sm text-body-sm text-on-surface file:mr-3 file:px-4 file:py-2 file:rounded-full file:border-0 file:bg-secondary file:text-on-secondary file:font-title-sm file:cursor-pointer hover:file:opacity-90">
<p class="font-body-sm text-body-sm text-on-surface-variant/70">JPG/PNG, do 8 MB.</p>
</div>
</div>
<div class="flex items-center gap-2 pt-2">
<button type="submit" class="bg-primary text-on-primary px-6 py-2.5 rounded-full font-title-sm text-title-sm hover:bg-primary-container transition-all active:scale-95 shadow-card">Zapisz</button>
<a href="{{ route('metering.index') }}" class="px-5 py-2.5 rounded-full font-title-sm text-title-sm text-on-surface-variant hover:bg-surface-container transition-all">Anuluj</a>
</div>
</form>
</div>
</x-app-layout>
@@ -0,0 +1,56 @@
<x-app-layout :active="'metering.index'" title="SoftSPM 2 - Edycja: {{ $item->EQU_NAME }}">
<div class="max-w-2xl space-y-stack-gap">
<div class="flex items-center gap-2">
<a href="{{ route('metering.show', $item) }}" class="font-body-sm text-body-sm text-on-surface-variant hover:text-primary">← Wróć</a>
<h1 class="font-headline-lg text-headline-lg text-on-surface">Edycja urządzenia opomiarowania</h1>
</div>
@if ($errors->any())
<div class="status-chip-alert px-3 py-2 rounded-lg font-body-sm text-body-sm">{{ $errors->first() }}</div>
@endif
<form method="POST" action="{{ route('metering.update', $item) }}" enctype="multipart/form-data"
class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card p-card-inner-padding space-y-4">
@csrf
@method('PUT')
<x-well-field name="name" label="Nazwa" :value="old('name', $item->EQU_NAME)" required />
<x-well-select name="manufacturer_id" label="Producent" :options="$manufacturers" :selected="old('manufacturer_id', $item->EQU_MCF_ID)" required placeholder="- wybierz -" />
<div class="space-y-1">
<label for="description" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Opis</label>
<textarea id="description" name="description" rows="3"
class="w-full bg-surface-container-low border border-outline-variant px-3 py-2 rounded focus:ring-1 focus:ring-primary focus:border-primary font-body-md text-on-surface">{{ old('description', $item->EQU_DESCRIPTION) }}</textarea>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div class="space-y-1">
<label for="catalogue_file" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Dokumentacja katalogowa</label>
<input id="catalogue_file" name="catalogue_file" type="file" accept=".pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx"
class="w-full font-body-sm text-body-sm text-on-surface file:mr-3 file:px-4 file:py-2 file:rounded-full file:border-0 file:bg-secondary file:text-on-secondary file:font-title-sm file:cursor-pointer hover:file:opacity-90">
@if ($document)
<p class="font-body-sm text-body-sm text-on-surface-variant/70">Obecny: <a href="{{ route('equipment.file', $document->EQF_ID) }}" target="_blank" class="text-secondary hover:underline">{{ $document->EQF_FILE_NAME }}</a>. Wgranie nowego zastąpi.</p>
@else
<p class="font-body-sm text-body-sm text-on-surface-variant/70">Brak. PDF lub Office, do 8 MB.</p>
@endif
</div>
<div class="space-y-1">
<label for="image_file" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Zdjęcie</label>
<input id="image_file" name="image_file" type="file" accept="image/*"
class="w-full font-body-sm text-body-sm text-on-surface file:mr-3 file:px-4 file:py-2 file:rounded-full file:border-0 file:bg-secondary file:text-on-secondary file:font-title-sm file:cursor-pointer hover:file:opacity-90">
@if ($image)
<p class="font-body-sm text-body-sm text-on-surface-variant/70">Obecne: <a href="{{ route('equipment.file', $image->EQF_ID) }}" target="_blank" class="text-secondary hover:underline">{{ $image->EQF_FILE_NAME }}</a>. Wgranie nowego zastąpi.</p>
@else
<p class="font-body-sm text-body-sm text-on-surface-variant/70">Brak. JPG/PNG, do 8 MB.</p>
@endif
</div>
</div>
<div class="flex items-center gap-2 pt-2">
<button type="submit" class="bg-primary text-on-primary px-6 py-2.5 rounded-full font-title-sm text-title-sm hover:bg-primary-container transition-all active:scale-95 shadow-card">Zapisz zmiany</button>
<a href="{{ route('metering.show', $item) }}" class="px-5 py-2.5 rounded-full font-title-sm text-title-sm text-on-surface-variant hover:bg-surface-container transition-all">Anuluj</a>
</div>
</form>
</div>
</x-app-layout>
@@ -0,0 +1,75 @@
<x-app-layout :active="'metering.index'" title="SoftSPM 2 - Katalog opomiarowania">
<div class="space-y-stack-gap">
@if (session('status'))
<div class="status-chip-ok px-3 py-2 rounded-lg font-body-sm text-body-sm">{{ session('status') }}</div>
@endif
<div class="flex items-end justify-between">
<div>
<h1 class="font-headline-lg text-headline-lg text-on-surface">Katalog opomiarowania</h1>
<p class="font-body-sm text-body-sm text-on-surface-variant mt-1">Wyników: <span class="font-data-label">{{ $items->total() }}</span></p>
</div>
<a href="{{ route('metering.create') }}"
class="bg-primary text-on-primary px-5 py-2.5 rounded-full font-title-sm text-title-sm hover:bg-primary-container transition-all active:scale-95 shadow-card">
Dodaj urządzenie
</a>
</div>
{{-- Wyszukiwarka --}}
<form method="GET" action="{{ route('metering.index') }}"
class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card p-card-inner-padding">
<div class="flex flex-wrap items-end gap-4">
<div class="space-y-1">
<label for="q" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Nazwa</label>
<input id="q" name="q" type="text" value="{{ $q }}" placeholder="nazwa urządzenia"
class="bg-surface-container-low border border-outline-variant px-3 py-2 rounded font-data-label text-on-surface">
</div>
<div class="space-y-1">
<label for="mcf" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Producent</label>
<select id="mcf" name="mcf" class="bg-surface-container-low border border-outline-variant px-3 py-2 rounded font-data-label text-on-surface">
<option value="{{ $mcfAll }}" {{ (int) $mcf === $mcfAll ? 'selected' : '' }}>Wszyscy</option>
@foreach ($manufacturers as $id => $name)
<option value="{{ $id }}" {{ (int) $mcf === (int) $id ? 'selected' : '' }}>{{ $name }}</option>
@endforeach
</select>
</div>
<button type="submit" class="bg-secondary text-on-secondary px-5 py-2.5 rounded-full font-title-sm text-title-sm hover:opacity-90 transition-all active:scale-95">Szukaj</button>
</div>
</form>
{{-- Tabela --}}
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
<table class="w-full text-left">
<thead class="bg-secondary/10 border-b border-outline-variant/40">
<tr class="font-data-label text-data-label text-on-surface-variant tracking-wide">
<th class="px-4 py-3 w-12">Lp.</th>
<x-sortable-th column="name">Nazwa</x-sortable-th>
<x-sortable-th column="mcf">Producent</x-sortable-th>
<th class="px-4 py-3 text-right">Akcje</th>
</tr>
</thead>
<tbody class="divide-y divide-outline-variant/30 font-body-md text-body-md">
@forelse ($items as $item)
<tr class="hover:bg-surface-container transition-colors">
<td class="px-4 py-3 font-data-label text-on-surface-variant">{{ $items->firstItem() + $loop->index }}.</td>
<td class="px-4 py-3 font-semibold text-on-surface">
<a href="{{ route('metering.show', $item) }}" class="hover:text-primary">{{ $item->EQU_NAME }}</a>
</td>
<td class="px-4 py-3">{{ $item->manufacturer?->MCF_NAME }}</td>
<td class="px-4 py-3 text-right whitespace-nowrap">
<a href="{{ route('metering.show', $item) }}" class="font-body-sm text-body-sm text-secondary hover:underline">Szczegóły</a>
<span class="text-outline-variant mx-1">·</span>
<a href="{{ route('metering.edit', $item) }}" class="font-body-sm text-body-sm text-secondary hover:underline">Edytuj</a>
</td>
</tr>
@empty
<tr><td colspan="4" class="px-4 py-8 text-center font-body-md text-body-md text-on-surface-variant">Brak urządzeń dla wybranych kryteriów.</td></tr>
@endforelse
</tbody>
</table>
</div>
<div>{{ $items->links() }}</div>
</div>
</x-app-layout>
@@ -0,0 +1,61 @@
<x-app-layout :active="'metering.index'" title="SoftSPM 2 - {{ $item->EQU_NAME }}">
<div class="space-y-stack-gap">
@if (session('status'))
<div class="status-chip-ok px-3 py-2 rounded-lg font-body-sm text-body-sm">{{ session('status') }}</div>
@endif
@if (session('error'))
<div class="status-chip-alert px-3 py-2 rounded-lg font-body-sm text-body-sm">{{ session('error') }}</div>
@endif
<div class="flex flex-wrap items-center justify-between gap-3">
<div class="flex items-center gap-2">
<a href="{{ route('metering.index') }}" class="font-body-sm text-body-sm text-on-surface-variant hover:text-primary">← Katalog opomiarowania</a>
<h1 class="font-headline-lg text-headline-lg text-on-surface">{{ $item->EQU_NAME }}</h1>
</div>
<div class="flex items-center gap-2">
<a href="{{ route('metering.edit', $item) }}"
class="bg-secondary text-on-secondary px-4 py-2 rounded-full font-title-sm text-title-sm hover:opacity-90 transition-all active:scale-95">Edytuj</a>
@unless ($inUse)
<x-confirm-delete :action="route('metering.destroy', $item)"
message="Czy na pewno usunąć urządzenie „{{ $item->EQU_NAME }}”?" />
@endunless
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-stack-gap">
{{-- Dane --}}
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card p-card-inner-padding">
<h2 class="font-title-sm text-title-sm text-tertiary mb-3">Dane</h2>
<dl class="grid grid-cols-3 gap-x-4 gap-y-2 font-body-md text-body-md">
<dt class="text-on-surface-variant">Nazwa</dt><dd class="col-span-2 font-semibold">{{ $item->EQU_NAME }}</dd>
<dt class="text-on-surface-variant">Producent</dt><dd class="col-span-2">{{ $item->manufacturer?->MCF_NAME ?? '-' }}</dd>
<dt class="text-on-surface-variant">Opis</dt><dd class="col-span-2">{!! $item->EQU_DESCRIPTION ? nl2br(e($item->EQU_DESCRIPTION)) : '-' !!}</dd>
@if ($document)
<dt class="text-on-surface-variant">Dokumentacja katalogowa</dt>
<dd class="col-span-2">
<a href="{{ route('equipment.file', $document->EQF_ID) }}" target="_blank"
class="text-secondary hover:underline break-all">{{ $document->EQF_FILE_NAME }}</a>
</dd>
@endif
</dl>
@if ($inUse)
<p class="mt-4 font-body-sm text-body-sm text-on-surface-variant/80">Urządzenie jest użyte w stanie opomiarowania - usunięcie zablokowane.</p>
@endif
</div>
{{-- Zdjęcie --}}
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card p-card-inner-padding">
<h2 class="font-title-sm text-title-sm text-tertiary mb-3">Zdjęcie</h2>
@if ($image)
<img src="{{ route('equipment.file', $image->EQF_ID) }}" alt="{{ $item->EQU_NAME }}"
class="max-w-full h-auto rounded border border-outline-variant/40">
@else
<p class="font-body-sm text-body-sm text-on-surface-variant py-6 text-center">Brak zdjęcia.</p>
@endif
</div>
</div>
</div>
</x-app-layout>