Initial commit
This commit is contained in:
@@ -0,0 +1,419 @@
|
||||
<x-guest-layout title="SoftSPM 2 - Logowanie">
|
||||
|
||||
<style>
|
||||
.login-screen { min-height: 100vh; display: flex; }
|
||||
|
||||
/* ===== Lewa kolumna - panel wizualny ===== */
|
||||
.login-visual {
|
||||
position: relative;
|
||||
width: 50%;
|
||||
overflow: hidden;
|
||||
/* Gradient turkus -> czerwień (jak hero na stronie głównej SoftSPM) */
|
||||
background:
|
||||
radial-gradient(circle at 86% 10%, rgba(255,255,255,.10), transparent 38%),
|
||||
radial-gradient(circle at 8% 92%, rgba(255,255,255,.07), transparent 44%),
|
||||
radial-gradient(120% 95% at 102% 102%, rgba(140,22,38,.50), transparent 58%),
|
||||
linear-gradient(118deg, #0a5366 0%, #094a5a 33%, #33263a 64%, #5e1f2c 88%, #6e1a26 100%);
|
||||
}
|
||||
.login-dots {
|
||||
position: absolute; inset: 0; z-index: 0;
|
||||
background-image: radial-gradient(rgba(255,255,255,.06) 1px, transparent 1.4px);
|
||||
background-size: 24px 24px;
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 28%, transparent 92%);
|
||||
mask-image: linear-gradient(to bottom, #000 28%, transparent 92%);
|
||||
}
|
||||
/* Okręgi w tle - bardziej wyraziste */
|
||||
.login-ring, .login-ring2 { position: absolute; z-index: 0; border-radius: 50%; }
|
||||
.login-ring { top: -180px; right: -150px; width: 470px; height: 470px; border: 1.5px solid rgba(255,255,255,.16); }
|
||||
.login-ring2 { top: -90px; right: -60px; width: 310px; height: 310px; border: 1px solid rgba(255,255,255,.11); }
|
||||
.login-ring3 { position: absolute; z-index: 0; bottom: -150px; left: -120px; width: 360px; height: 360px; border-radius: 50%; border: 1.5px solid rgba(255,255,255,.10); }
|
||||
.login-mesh { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; pointer-events: none; }
|
||||
.login-brand { position: relative; z-index: 2; padding: 2.75rem 3rem; max-width: min(1200px, 90%); }
|
||||
.login-brand .over {
|
||||
color: #4fc3e8; font-family: 'JetBrains Mono', monospace; font-size: 12px;
|
||||
letter-spacing: .18em; text-transform: uppercase; font-weight: 600;
|
||||
}
|
||||
.login-brand h1 {
|
||||
color: #fff; font-weight: 700; line-height: 1.18; margin-top: .9rem;
|
||||
font-size: clamp(26px, 3.2vw, 44px);
|
||||
text-shadow: 0 2px 24px rgba(0,0,0,.25);
|
||||
}
|
||||
|
||||
/* ===== Prawa kolumna - formularz ===== */
|
||||
.login-form-panel {
|
||||
width: 50%; display: flex; align-items: center; justify-content: center;
|
||||
padding: 2rem; background: #f7fafb;
|
||||
}
|
||||
|
||||
/* ===== Responsywnie: < 860px pionowo ===== */
|
||||
@media (max-width: 860px) {
|
||||
.login-screen { flex-direction: column; }
|
||||
.login-visual { width: 100%; height: 200px; }
|
||||
.login-brand { padding: 1.25rem 1.5rem; }
|
||||
.login-form-panel { width: 100%; }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.login-mesh { opacity: .9; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="login-screen">
|
||||
|
||||
{{-- ===== LEWA: wizual + animacja ===== --}}
|
||||
<div class="login-visual">
|
||||
<div class="login-dots"></div>
|
||||
<div class="login-ring"></div>
|
||||
<div class="login-ring2"></div>
|
||||
<div class="login-ring3"></div>
|
||||
<canvas id="login-mesh" class="login-mesh" aria-hidden="true" data-mode="text" data-text="SoftSPM"></canvas>
|
||||
|
||||
<div class="login-brand">
|
||||
<p class="over">Submersible Pumps Management</p>
|
||||
<h1>Inteligentny Nadzór Eksploatacji Pomp Głębinowych i Raportowanie</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ===== PRAWA: formularz ===== --}}
|
||||
<div class="login-form-panel">
|
||||
<div class="w-full" style="max-width: 360px;">
|
||||
|
||||
{{-- Logo - link do softspm.com (ta sama zakładka) --}}
|
||||
<div class="mb-7">
|
||||
<a href="https://softspm.com" class="inline-block">
|
||||
<img src="{{ asset('images/logo.png') }}" alt="SoftSPM" style="width: 220px; max-width: 100%;" class="h-auto">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2 class="font-headline-md text-headline-md text-on-surface">Logowanie</h2>
|
||||
<p class="font-body-sm text-body-sm text-on-surface-variant mt-1 mb-6">Zaloguj się do systemu SoftSPM</p>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="status-chip-alert px-3 py-2 rounded-lg font-body-sm text-body-sm mb-4">{{ $errors->first() }}</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('login.attempt') }}" class="space-y-4" style="padding: 0;">
|
||||
@csrf
|
||||
|
||||
{{-- Nazwa użytkownika --}}
|
||||
<div class="space-y-1">
|
||||
<label for="username" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Nazwa użytkownika</label>
|
||||
<div class="relative">
|
||||
<svg class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-on-surface-variant/60" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>
|
||||
</svg>
|
||||
<input id="username" name="username" type="text" required autofocus autocomplete="username"
|
||||
value="{{ old('username') }}" placeholder="Podaj nazwę użytkownika"
|
||||
class="w-full bg-white border border-outline-variant pl-10 pr-3 py-3 rounded-lg focus:ring-1 focus:ring-secondary focus:border-secondary font-body-md text-on-surface transition-all">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Hasło --}}
|
||||
<div class="space-y-1">
|
||||
<label for="password" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Hasło</label>
|
||||
<div class="relative">
|
||||
<svg class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-on-surface-variant/60" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>
|
||||
</svg>
|
||||
<input id="password" name="password" type="password" required autocomplete="current-password"
|
||||
placeholder="••••••••"
|
||||
class="w-full bg-white border border-outline-variant pl-10 pr-3 py-3 rounded-lg focus:ring-1 focus:ring-secondary focus:border-secondary font-body-md text-on-surface transition-all">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Oddział - ukryty na czas testów (config softspm.login_department); odwracalne (ustaw null) --}}
|
||||
@unless (config('softspm.login_department'))
|
||||
<div class="space-y-1">
|
||||
<label for="department" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Oddział</label>
|
||||
<div class="relative">
|
||||
<svg class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-on-surface-variant/60" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M3 21h18"/><path d="M5 21V7l8-4v18"/><path d="M19 21V11l-6-4"/><path d="M9 9v.01M9 12v.01M9 15v.01M9 18v.01"/>
|
||||
</svg>
|
||||
<select id="department" name="department" required
|
||||
class="w-full bg-white border border-outline-variant pl-10 pr-3 py-3 rounded-lg focus:ring-1 focus:ring-secondary focus:border-secondary font-body-md text-on-surface transition-all">
|
||||
@foreach (config('softspm.departments') as $dep)
|
||||
<option value="{{ $dep }}" {{ old('department') === $dep ? 'selected' : '' }}>{{ $dep }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@endunless
|
||||
|
||||
<x-btn type="submit" variant="primary" class="w-full mt-2">Zaloguj</x-btn>
|
||||
</form>
|
||||
|
||||
<p class="mt-8 text-center font-body-sm text-body-sm text-on-surface-variant/70">
|
||||
© SoftSPM {{ date('Y') }} · v.2.0
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ===== Animacja "flowing wave mesh" (Canvas 2D, bez bibliotek) ===== --}}
|
||||
<script>
|
||||
(function () {
|
||||
const canvas = document.getElementById('login-mesh');
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext('2d', { alpha: true });
|
||||
|
||||
// ===== CONFIG (wartości referencyjne; data-mode/data-text nadpisują) =====
|
||||
const CONFIG = {
|
||||
mode: canvas.dataset.mode || 'text',
|
||||
text: canvas.dataset.text || 'SoftSPM',
|
||||
grid: 7,
|
||||
alphaThreshold: 120,
|
||||
fontWeight: 700,
|
||||
textHeightRatio: 0.17,
|
||||
textAnchor: { x: 0.07, y: 0.90 },
|
||||
amplitude: 1.7,
|
||||
speed: 0.0016,
|
||||
color: { r: 41, g: 171, b: 226 },
|
||||
opacity: 0.85,
|
||||
glow: 8,
|
||||
lineWidth: 1,
|
||||
nodeRadius: 1.15,
|
||||
drawNodes: true,
|
||||
edgeFalloff: 0.55,
|
||||
buckets: 6,
|
||||
// tryb wave
|
||||
bandCenter: 0.72,
|
||||
bandWidth: 0.40,
|
||||
waveCols: 90,
|
||||
waveRows: 13,
|
||||
connectorEvery: 3,
|
||||
waves: [
|
||||
{ amp: 14, freq: 1.1, speed: 0.55, phase: 0.0 },
|
||||
{ amp: 8, freq: 2.3, speed: -0.9, phase: 1.7 },
|
||||
{ amp: 5, freq: 3.7, speed: 1.35, phase: 3.1 },
|
||||
],
|
||||
};
|
||||
|
||||
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
const rgba = (a) => 'rgba(' + CONFIG.color.r + ',' + CONFIG.color.g + ',' + CONFIG.color.b + ',' + a + ')';
|
||||
|
||||
let dpr = 1, cssW = 0, cssH = 0;
|
||||
let nodes = [], edges = [], spotX = 0;
|
||||
let particles = [];
|
||||
let rafId = null, resizeTimer = null, start = 0;
|
||||
|
||||
function setSize() {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
cssW = Math.max(1, Math.round(rect.width));
|
||||
cssH = Math.max(1, Math.round(rect.height));
|
||||
dpr = Math.min(window.devicePixelRatio || 1, 2); // ostre linie na HiDPI
|
||||
canvas.width = Math.round(cssW * dpr);
|
||||
canvas.height = Math.round(cssH * dpr);
|
||||
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||
}
|
||||
|
||||
// ===== Konstelacja tła (dryfujące punkty + linie - jak hero strony głównej) =====
|
||||
function makeParticles() {
|
||||
const count = Math.round(cssW * cssH / 15000);
|
||||
particles = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
particles.push({
|
||||
x: Math.random() * cssW, y: Math.random() * cssH,
|
||||
vx: (Math.random() - 0.5) * 0.22, vy: (Math.random() - 0.5) * 0.22,
|
||||
});
|
||||
}
|
||||
}
|
||||
function updateParticles() {
|
||||
for (const p of particles) {
|
||||
p.x += p.vx; p.y += p.vy;
|
||||
if (p.x < 0 || p.x > cssW) p.vx = -p.vx;
|
||||
if (p.y < 0 || p.y > cssH) p.vy = -p.vy;
|
||||
}
|
||||
}
|
||||
function drawConstellation() {
|
||||
const maxD = 155, maxD2 = maxD * maxD;
|
||||
ctx.lineWidth = 1;
|
||||
for (let i = 0; i < particles.length; i++) {
|
||||
const a = particles[i];
|
||||
for (let j = i + 1; j < particles.length; j++) {
|
||||
const b = particles[j];
|
||||
const dx = a.x - b.x, dy = a.y - b.y, d2 = dx * dx + dy * dy;
|
||||
if (d2 < maxD2) {
|
||||
ctx.strokeStyle = 'rgba(180,214,228,' + ((1 - Math.sqrt(d2) / maxD) * 0.16) + ')';
|
||||
ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.lineTo(b.x, b.y); ctx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.fillStyle = 'rgba(205,230,240,.32)';
|
||||
ctx.beginPath();
|
||||
for (const p of particles) { ctx.moveTo(p.x + 1.3, p.y); ctx.arc(p.x, p.y, 1.3, 0, Math.PI * 2); }
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
// ===== Budowa modelu z napisu =====
|
||||
function buildText() {
|
||||
const off = document.createElement('canvas');
|
||||
off.width = cssW; off.height = cssH;
|
||||
const o = off.getContext('2d');
|
||||
const fontSize = Math.max(12, Math.round(cssH * CONFIG.textHeightRatio));
|
||||
o.clearRect(0, 0, cssW, cssH);
|
||||
o.fillStyle = '#fff';
|
||||
o.textBaseline = 'alphabetic';
|
||||
o.textAlign = 'left';
|
||||
o.font = CONFIG.fontWeight + ' ' + fontSize + "px 'Inter', system-ui, sans-serif";
|
||||
o.fillText(CONFIG.text, cssW * CONFIG.textAnchor.x, cssH * CONFIG.textAnchor.y);
|
||||
const data = o.getImageData(0, 0, cssW, cssH).data;
|
||||
|
||||
nodes = []; edges = [];
|
||||
const map = new Map();
|
||||
const g = CONFIG.grid;
|
||||
let minX = 1e9, maxX = -1e9;
|
||||
for (let y = 0; y < cssH; y += g) {
|
||||
const row = (y / g) | 0;
|
||||
for (let x = 0; x < cssW; x += g) {
|
||||
if (data[(y * cssW + x) * 4 + 3] >= CONFIG.alphaThreshold) {
|
||||
const col = (x / g) | 0;
|
||||
map.set(col + '_' + row, nodes.length);
|
||||
nodes.push({ bx: x, by: y, x: x, y: y, col: col, row: row, ph: col * 0.7 + row * 0.9 });
|
||||
if (x < minX) minX = x;
|
||||
if (x > maxX) maxX = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
buildEdges(map, true);
|
||||
spotX = nodes.length ? (minX + maxX) / 2 : cssW * 0.3;
|
||||
}
|
||||
|
||||
// ===== Budowa modelu pasma fal =====
|
||||
function buildWave() {
|
||||
nodes = []; edges = [];
|
||||
const map = new Map();
|
||||
const cols = CONFIG.waveCols, rows = CONFIG.waveRows;
|
||||
const bandTop = cssH * (CONFIG.bandCenter - CONFIG.bandWidth / 2);
|
||||
const bandH = cssH * CONFIG.bandWidth;
|
||||
for (let r = 0; r < rows; r++) {
|
||||
for (let c = 0; c < cols; c++) {
|
||||
const x = cols > 1 ? (c / (cols - 1)) * cssW : 0;
|
||||
const y = rows > 1 ? bandTop + (r / (rows - 1)) * bandH : bandTop;
|
||||
map.set(c + '_' + r, nodes.length);
|
||||
nodes.push({ bx: x, by: y, x: x, y: y, col: c, row: r, ph: c * 0.3 + r * 0.5 });
|
||||
}
|
||||
}
|
||||
buildEdges(map, false);
|
||||
spotX = cssW * 0.5;
|
||||
}
|
||||
|
||||
function buildEdges(map, triangular) {
|
||||
for (const n of nodes) {
|
||||
const i = map.get(n.col + '_' + n.row);
|
||||
const right = map.get((n.col + 1) + '_' + n.row);
|
||||
if (right !== undefined) edges.push([i, right]);
|
||||
if (triangular) {
|
||||
const down = map.get(n.col + '_' + (n.row + 1));
|
||||
const diag = map.get((n.col + 1) + '_' + (n.row + 1));
|
||||
if (down !== undefined) edges.push([i, down]);
|
||||
if (diag !== undefined) edges.push([i, diag]);
|
||||
} else if (n.col % CONFIG.connectorEvery === 0) {
|
||||
const down = map.get(n.col + '_' + (n.row + 1));
|
||||
if (down !== undefined) edges.push([i, down]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function build() {
|
||||
if (CONFIG.mode === 'wave') buildWave(); else buildText();
|
||||
}
|
||||
|
||||
// ===== Ruch węzłów =====
|
||||
function updateNodes(t) {
|
||||
if (CONFIG.mode === 'wave') {
|
||||
for (const n of nodes) {
|
||||
let dy = 0;
|
||||
for (const w of CONFIG.waves) {
|
||||
dy += w.amp * Math.sin((n.col / CONFIG.waveCols) * Math.PI * 2 * w.freq + t * w.speed + w.phase + n.row * 0.1);
|
||||
}
|
||||
n.x = n.bx;
|
||||
n.y = n.by + dy;
|
||||
}
|
||||
} else {
|
||||
const A = CONFIG.amplitude;
|
||||
for (const n of nodes) {
|
||||
n.x = n.bx + A * Math.sin(t + n.ph);
|
||||
n.y = n.by + A * Math.cos(t * 1.1 + n.ph * 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Rysowanie (kwantyzacja jasności do kubełków - 1 stroke/kubełek) =====
|
||||
function draw() {
|
||||
ctx.clearRect(0, 0, cssW, cssH);
|
||||
ctx.shadowBlur = 0;
|
||||
drawConstellation(); // tło konstelacji (bez poświaty)
|
||||
const B = CONFIG.buckets;
|
||||
const bins = [];
|
||||
for (let i = 0; i < B; i++) bins.push([]);
|
||||
|
||||
for (const e of edges) {
|
||||
const a = nodes[e[0]], b = nodes[e[1]];
|
||||
const nx = Math.abs((a.x + b.x) / 2 - spotX) / cssW; // odległość poziomą od reflektora
|
||||
let f = 1 - CONFIG.edgeFalloff * (2 * nx) * (2 * nx); // reflektor
|
||||
if (f < 0.05) f = 0.05; else if (f > 1) f = 1;
|
||||
bins[Math.min(B - 1, Math.floor((1 - f) * B))].push(e);
|
||||
}
|
||||
|
||||
ctx.lineWidth = CONFIG.lineWidth;
|
||||
ctx.lineCap = 'round';
|
||||
ctx.shadowColor = rgba(CONFIG.opacity);
|
||||
ctx.shadowBlur = CONFIG.glow;
|
||||
|
||||
for (let bi = 0; bi < B; bi++) {
|
||||
const list = bins[bi];
|
||||
if (!list.length) continue;
|
||||
ctx.strokeStyle = rgba(CONFIG.opacity * (1 - (bi + 0.5) / B));
|
||||
ctx.beginPath();
|
||||
for (const e of list) {
|
||||
const a = nodes[e[0]], b = nodes[e[1]];
|
||||
ctx.moveTo(a.x, a.y); ctx.lineTo(b.x, b.y);
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
if (CONFIG.drawNodes) {
|
||||
ctx.shadowBlur = CONFIG.glow * 0.6;
|
||||
ctx.fillStyle = rgba(CONFIG.opacity);
|
||||
ctx.beginPath();
|
||||
const r = CONFIG.nodeRadius;
|
||||
for (const n of nodes) {
|
||||
ctx.moveTo(n.x + r, n.y);
|
||||
ctx.arc(n.x, n.y, r, 0, Math.PI * 2);
|
||||
}
|
||||
ctx.fill();
|
||||
}
|
||||
ctx.shadowBlur = 0;
|
||||
}
|
||||
|
||||
function loop(now) {
|
||||
updateNodes((now - start) * CONFIG.speed);
|
||||
updateParticles();
|
||||
draw();
|
||||
rafId = requestAnimationFrame(loop);
|
||||
}
|
||||
|
||||
function init() {
|
||||
if (rafId) { cancelAnimationFrame(rafId); rafId = null; }
|
||||
setSize();
|
||||
makeParticles();
|
||||
build();
|
||||
if (reduce) { updateNodes(0); draw(); return; } // statyczny render
|
||||
start = performance.now();
|
||||
rafId = requestAnimationFrame(loop);
|
||||
}
|
||||
|
||||
function onResize() {
|
||||
clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(init, 150);
|
||||
}
|
||||
window.addEventListener('resize', onResize);
|
||||
|
||||
// Czekaj na fonty (czytelny napis), potem buduj model.
|
||||
if (document.fonts && document.fonts.ready) {
|
||||
document.fonts.ready.then(init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</x-guest-layout>
|
||||
@@ -0,0 +1,90 @@
|
||||
<x-app-layout :active="'aggregate-selection.index'" title="SoftSPM 2 - Wybór agregatu pompowego">
|
||||
@php $fmt = fn ($v, $d = 2) => $v === null || $v === '' ? '-' : number_format(round((float) $v, $d), $d, ',', ' '); @endphp
|
||||
|
||||
<div class="space-y-stack-gap">
|
||||
|
||||
<div>
|
||||
<h1 class="font-headline-lg text-headline-lg text-on-surface">Wybór agregatu pompowego</h1>
|
||||
<p class="font-body-sm text-body-sm text-on-surface-variant mt-1">Wysokość podnoszenia dla posiadanych typów pomp</p>
|
||||
</div>
|
||||
|
||||
{{-- Parametry doboru --}}
|
||||
<form method="GET" action="{{ route('aggregate-selection.index') }}"
|
||||
class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card p-card-inner-padding">
|
||||
<p class="font-data-label text-data-label text-on-surface-variant tracking-wide mb-3">Wprowadź parametry poszukiwanej pompy</p>
|
||||
<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">Zadana wydajność Q<sub>sz</sub> [m³/h]</label>
|
||||
<input id="q" name="q" type="text" inputmode="decimal" required value="{{ $q }}" placeholder="np. 120"
|
||||
class="bg-surface-container-low border border-outline-variant px-3 py-2 rounded font-data-label text-on-surface focus:ring-1 focus:ring-primary focus:border-primary">
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<label for="h" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Zadana wysokość podnoszenia H [m] <span class="normal-case text-on-surface-variant/60">(opcjonalnie)</span></label>
|
||||
<input id="h" name="h" type="text" inputmode="decimal" value="{{ $h }}" placeholder="np. 80"
|
||||
class="bg-surface-container-low border border-outline-variant px-3 py-2 rounded font-data-label text-on-surface focus:ring-1 focus:ring-primary focus:border-primary">
|
||||
</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>
|
||||
|
||||
@if ($searched)
|
||||
{{-- Wykres nałożonych krzywych H --}}
|
||||
@if ($chart)
|
||||
<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-2">Wysokość podnoszenia dla posiadanych typów pomp</h2>
|
||||
<x-selection-chart :chart="$chart" :q="$qVal" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Lista pomp dla zadanego Q (ranking po sprawności) --}}
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="px-card-inner-padding pt-card-inner-padding">
|
||||
<h2 class="font-title-sm text-title-sm text-tertiary">
|
||||
Lista pomp dla zadanego Q
|
||||
<span class="font-body-sm text-body-sm text-on-surface-variant">(malejąco wg sprawności)</span>
|
||||
</h2>
|
||||
</div>
|
||||
<table class="w-full text-left mt-2">
|
||||
<thead class="bg-secondary/10 border-y 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>
|
||||
<th class="px-4 py-3">Typ pompy</th>
|
||||
<th class="px-4 py-3">Typ silnika</th>
|
||||
<th class="px-4 py-3 text-right">Q<sub>n</sub> [m³/h]</th>
|
||||
<th class="px-4 py-3 text-right">Q<sub>sz</sub> [m³/h]</th>
|
||||
<th class="px-4 py-3 text-right">H(Q<sub>sz</sub>) [m]</th>
|
||||
<th class="px-4 py-3 text-right">η(Q<sub>sz</sub>) [-]</th>
|
||||
<th class="px-4 py-3 text-right">P<sub>s</sub> [kW]</th>
|
||||
<th class="px-4 py-3 text-right">Napięcie [V]</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-outline-variant/30 font-body-md text-body-md">
|
||||
@forelse ($rows as $row)
|
||||
<tr class="hover:bg-surface-container transition-colors cursor-pointer"
|
||||
onclick="location.href='{{ route('aggregates.show', ['pump' => $row->pump_id] + ($row->engine_id ? ['engine' => $row->engine_id] : [])) }}'">
|
||||
<td class="px-4 py-3 font-data-label text-on-surface-variant">{{ $row->lp }}.</td>
|
||||
<td class="px-4 py-3 font-semibold text-on-surface">{{ $row->pump_name }}</td>
|
||||
<td class="px-4 py-3">{{ $row->engine_name ?? '-' }}</td>
|
||||
<td class="px-4 py-3 text-right font-data-label">{{ $fmt($row->q_n, 3) }}</td>
|
||||
<td class="px-4 py-3 text-right font-data-label">{{ $fmt($qVal, 3) }}</td>
|
||||
<td class="px-4 py-3 text-right font-data-label">{{ $fmt($row->h_val, 2) }}</td>
|
||||
<td class="px-4 py-3 text-right font-data-label">{{ $fmt($row->eta_val, 2) }}</td>
|
||||
<td class="px-4 py-3 text-right font-data-label">{{ $fmt($row->power, 2) }}</td>
|
||||
<td class="px-4 py-3 text-right font-data-label">{{ $row->voltage ?? '-' }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="9" class="px-4 py-8 text-center font-body-md text-body-md text-on-surface-variant">
|
||||
Brak pomp spełniających wprowadzone parametry.
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,46 @@
|
||||
<x-app-layout :active="'aggregates.index'" title="SoftSPM 2 - Przypisanie silników">
|
||||
<div class="max-w-3xl space-y-stack-gap">
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('aggregates.show', $pump) }}" 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">Silniki dla pompy: {{ $pump->PUP_NAME }}</h1>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('aggregates.update', $pump) }}"
|
||||
class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<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-2 w-12"></th>
|
||||
<th class="px-4 py-2">Typ silnika</th>
|
||||
<th class="px-4 py-2 text-right">Napięcie [V]</th>
|
||||
<th class="px-4 py-2 text-right">Moc [kW]</th>
|
||||
<th class="px-4 py-2">Producent</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-outline-variant/30 font-body-md text-body-md">
|
||||
@foreach ($allEngines as $e)
|
||||
<tr class="hover:bg-surface-container transition-colors">
|
||||
<td class="px-4 py-2">
|
||||
<input type="checkbox" name="engines[]" value="{{ $e->ENG_ID }}"
|
||||
{{ in_array((int) $e->ENG_ID, $assigned, true) ? 'checked' : '' }}
|
||||
class="w-4 h-4 accent-secondary">
|
||||
</td>
|
||||
<td class="px-4 py-2 font-semibold text-on-surface">{{ $e->ENG_NAME }}</td>
|
||||
<td class="px-4 py-2 text-right font-data-label">{{ $e->ENG_VOLTAGE }}</td>
|
||||
<td class="px-4 py-2 text-right font-data-label">{{ $e->ENG_POWER }}</td>
|
||||
<td class="px-4 py-2">{{ $e->manufacturer?->MCF_NAME }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="flex items-center gap-2 p-card-inner-padding border-t border-outline-variant/40">
|
||||
<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('aggregates.show', $pump) }}" 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,79 @@
|
||||
<x-app-layout :active="'aggregates.index'" title="SoftSPM 2 - Katalog agregatow">
|
||||
@php $fmt = fn ($v, $d = 3) => $v === null ? '-' : number_format(round((float) $v, $d), $d, ',', ' '); @endphp
|
||||
|
||||
<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>
|
||||
<h1 class="font-headline-lg text-headline-lg text-on-surface">Katalog agregatow</h1>
|
||||
<p class="font-body-sm text-body-sm text-on-surface-variant mt-1">Wynikow: <span class="font-data-label">{{ $aggregates->total() }}</span></p>
|
||||
</div>
|
||||
|
||||
{{-- Wyszukiwarka pomp --}}
|
||||
<form method="GET" action="{{ route('aggregates.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="by" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Kryterium</label>
|
||||
<select id="by" name="by" class="bg-surface-container-low border border-outline-variant px-3 py-2 rounded font-data-label text-on-surface">
|
||||
<option value="name" {{ $by === 'name' ? 'selected' : '' }}>Typ pompy</option>
|
||||
<option value="flow" {{ $by === 'flow' ? 'selected' : '' }}>Wydajnosc Q</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<label for="q" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Szukaj</label>
|
||||
<input id="q" name="q" type="text" value="{{ $q }}" placeholder="typ lub Q"
|
||||
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 active:scale-95 transition-all">Szukaj</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<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">Typ pompy</x-sortable-th>
|
||||
<x-sortable-th column="flow" align="right">Q<sub>n</sub> [m³/h]</x-sortable-th>
|
||||
<x-sortable-th column="mcf">Producent</x-sortable-th>
|
||||
<x-sortable-th column="engines" align="right">Silniki</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 ($aggregates as $a)
|
||||
<tr class="hover:bg-surface-container transition-colors">
|
||||
<td class="px-4 py-3 font-data-label text-on-surface-variant">{{ $aggregates->firstItem() + $loop->index }}.</td>
|
||||
<td class="px-4 py-3 font-semibold text-on-surface">
|
||||
<a href="{{ route('aggregates.show', $a) }}" class="hover:text-primary">{{ $a->PUP_NAME }}</a>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-right font-data-label">{{ $fmt($a->PUP_Q_N) }}</td>
|
||||
<td class="px-4 py-3">{{ $a->manufacturer?->MCF_NAME }}</td>
|
||||
<td class="px-4 py-3 text-right font-data-label">{{ $a->engines_count }}</td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
<a href="{{ route('aggregates.show', $a) }}" class="font-body-sm text-body-sm text-secondary hover:underline">Szczegoly</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="px-4 py-8 text-center font-body-md text-body-md text-on-surface-variant">Brak pomp dla wybranych kryteriow.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>{{ $aggregates->links() }}</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,96 @@
|
||||
<x-app-layout :active="'aggregates.index'" title="SoftSPM 2 - Agregat {{ $pump->PUP_NAME }}">
|
||||
@php $fmt = fn ($v, $d = 2) => $v === null ? '-' : number_format(round((float) $v, $d), $d, ',', ' '); @endphp
|
||||
|
||||
<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 flex-wrap items-center justify-between gap-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('aggregates.index') }}" class="font-body-sm text-body-sm text-on-surface-variant hover:text-primary">← Katalog agregatow</a>
|
||||
<h1 class="font-headline-lg text-headline-lg text-on-surface">{{ $pump->PUP_NAME }}</h1>
|
||||
</div>
|
||||
<a href="{{ route('aggregates.edit', $pump) }}"
|
||||
class="bg-secondary text-on-secondary px-4 py-2 rounded-full font-title-sm text-title-sm hover:opacity-90 active:scale-95 transition-all">Przypisz silniki</a>
|
||||
</div>
|
||||
|
||||
{{-- Wykresy --}}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-stack-gap">
|
||||
<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-2">Wysokosc podnoszenia</h2>
|
||||
@if ($hDrawable)
|
||||
<x-pump-curve :plot="$hPlot" x-label="Q [m³/h]" y-label="H [m]" />
|
||||
@else
|
||||
<p class="font-body-sm text-body-sm text-on-surface-variant py-6 text-center">Brak parametrow energetycznych dla pompy.</p>
|
||||
@endif
|
||||
</div>
|
||||
<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-2">Sprawnosc</h2>
|
||||
@if ($etaDrawable)
|
||||
<x-pump-curve :plot="$etaPlot" x-label="Q [m³/h]" y-label="η [-]" />
|
||||
@else
|
||||
<p class="font-body-sm text-body-sm text-on-surface-variant py-6 text-center">Brak parametrow energetycznych dla pompy.</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Silniki dla pompy (kolumny wg oryginalu: Typ silnika / Napiecie / Producent) --}}
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="px-card-inner-padding pt-card-inner-padding">
|
||||
<h2 class="font-title-sm text-title-sm text-tertiary">Lista silnikow dla pompy: {{ $pump->PUP_NAME }}</h2>
|
||||
</div>
|
||||
<table class="w-full text-left mt-2">
|
||||
<thead class="bg-secondary/10 border-y border-outline-variant/40">
|
||||
<tr class="font-data-label text-data-label text-on-surface-variant tracking-wide">
|
||||
<th class="px-4 py-2">Typ silnika</th>
|
||||
<th class="px-4 py-2 text-right">Napiecie [V]</th>
|
||||
<th class="px-4 py-2">Producent</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-outline-variant/30 font-body-md text-body-md">
|
||||
@forelse ($engines as $e)
|
||||
<tr class="cursor-pointer transition-colors {{ $engine && $engine->ENG_ID === $e->ENG_ID ? 'bg-secondary-container/60' : 'hover:bg-surface-container' }}"
|
||||
onclick="location.href='{{ route('aggregates.show', ['pump' => $pump->PUP_ID, 'engine' => $e->ENG_ID]) }}'">
|
||||
<td class="px-4 py-2 font-semibold text-on-surface">{{ $e->ENG_NAME }}</td>
|
||||
<td class="px-4 py-2 text-right font-data-label">{{ $e->ENG_VOLTAGE }}</td>
|
||||
<td class="px-4 py-2">{{ $e->MCF_NAME }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="3" class="px-4 py-4 text-center font-body-sm text-on-surface-variant">Brak przypisanych silnikow - kliknij "Przypisz silniki".</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{-- Karta zestawu pompa + silnik --}}
|
||||
@if ($engine)
|
||||
<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-primary mb-3">{{ $pump->PUP_NAME }} + {{ $engine->ENG_NAME }}</h2>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-6">
|
||||
<a href="{{ route('pumps.show', $pump) }}" class="block group">
|
||||
<p class="font-data-label text-data-label text-on-surface-variant tracking-wide mb-2 group-hover:text-primary">Pompa →</p>
|
||||
<dl class="grid grid-cols-2 gap-y-1 font-body-md text-body-md">
|
||||
<dt class="text-on-surface-variant">Typ pompy</dt><dd class="font-semibold">{{ $pump->PUP_NAME }}</dd>
|
||||
<dt class="text-on-surface-variant">Producent</dt><dd>{{ $pump->manufacturer?->MCF_NAME ?? '-' }}</dd>
|
||||
<dt class="text-on-surface-variant">Q<sub>n</sub> [m³/h]</dt><dd class="font-data-label">{{ $fmt($pump->PUP_Q_N, 3) }}</dd>
|
||||
<dt class="text-on-surface-variant">H<sub>p</sub> [m]</dt><dd class="font-data-label">{{ $fmt($pump->PUP_H_P) }}</dd>
|
||||
<dt class="text-on-surface-variant">η<sub>max</sub> [-]</dt><dd class="font-data-label">{{ $fmt($pump->PUP_ETA_MAX) }}</dd>
|
||||
</dl>
|
||||
</a>
|
||||
<a href="{{ route('engines.show', $engine) }}" class="block group">
|
||||
<p class="font-data-label text-data-label text-on-surface-variant tracking-wide mb-2 group-hover:text-primary">Silnik →</p>
|
||||
<dl class="grid grid-cols-2 gap-y-1 font-body-md text-body-md">
|
||||
<dt class="text-on-surface-variant">Typ silnika</dt><dd class="font-semibold">{{ $engine->ENG_NAME }}</dd>
|
||||
<dt class="text-on-surface-variant">Producent</dt><dd>{{ $engine->manufacturer?->MCF_NAME ?? '-' }}</dd>
|
||||
<dt class="text-on-surface-variant">Napiecie [V]</dt><dd class="font-data-label">{{ $engine->ENG_VOLTAGE ?? '-' }}</dd>
|
||||
<dt class="text-on-surface-variant">Moc [kW]</dt><dd class="font-data-label">{{ $fmt($engine->ENG_POWER) }}</dd>
|
||||
<dt class="text-on-surface-variant">Sprawnosc η [-]</dt><dd class="font-data-label">{{ $fmt($engine->ENG_ETA) }}</dd>
|
||||
</dl>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,47 @@
|
||||
<x-app-layout :active="'cables.index'" title="SoftSPM 2 - Nowy typ kabla">
|
||||
<div class="max-w-2xl space-y-stack-gap">
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('cables.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">Nowy typ kabla</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('cables.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="Typ kabla" :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('cables.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="'cables.index'" title="SoftSPM 2 - Edycja: {{ $cable->EQU_NAME }}">
|
||||
<div class="max-w-2xl space-y-stack-gap">
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('cables.show', $cable) }}" 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 typu kabla</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('cables.update', $cable) }}" 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="Typ kabla" :value="old('name', $cable->EQU_NAME)" required />
|
||||
<x-well-select name="manufacturer_id" label="Producent" :options="$manufacturers" :selected="old('manufacturer_id', $cable->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', $cable->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('cables.show', $cable) }}" 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="'cables.index'" title="SoftSPM 2 - Katalog kabli">
|
||||
<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 kabli</h1>
|
||||
<p class="font-body-sm text-body-sm text-on-surface-variant mt-1">Wyników: <span class="font-data-label">{{ $cables->total() }}</span></p>
|
||||
</div>
|
||||
<a href="{{ route('cables.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 typ kabla
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{-- Wyszukiwarka --}}
|
||||
<form method="GET" action="{{ route('cables.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">Typ kabla</label>
|
||||
<input id="q" name="q" type="text" value="{{ $q }}" placeholder="nazwa / typ"
|
||||
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">Typ kabla</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 ($cables as $cable)
|
||||
<tr class="hover:bg-surface-container transition-colors">
|
||||
<td class="px-4 py-3 font-data-label text-on-surface-variant">{{ $cables->firstItem() + $loop->index }}.</td>
|
||||
<td class="px-4 py-3 font-semibold text-on-surface">
|
||||
<a href="{{ route('cables.show', $cable) }}" class="hover:text-primary">{{ $cable->EQU_NAME }}</a>
|
||||
</td>
|
||||
<td class="px-4 py-3">{{ $cable->manufacturer?->MCF_NAME }}</td>
|
||||
<td class="px-4 py-3 text-right whitespace-nowrap">
|
||||
<a href="{{ route('cables.show', $cable) }}" 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('cables.edit', $cable) }}" 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 kabli dla wybranych kryteriów.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>{{ $cables->links() }}</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,61 @@
|
||||
<x-app-layout :active="'cables.index'" title="SoftSPM 2 - {{ $cable->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('cables.index') }}" class="font-body-sm text-body-sm text-on-surface-variant hover:text-primary">← Katalog kabli</a>
|
||||
<h1 class="font-headline-lg text-headline-lg text-on-surface">{{ $cable->EQU_NAME }}</h1>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('cables.edit', $cable) }}"
|
||||
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 ($inStock)
|
||||
<x-confirm-delete :action="route('cables.destroy', $cable)"
|
||||
message="Czy na pewno usunąć typ kabla „{{ $cable->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">Typ kabla</dt><dd class="col-span-2 font-semibold">{{ $cable->EQU_NAME }}</dd>
|
||||
<dt class="text-on-surface-variant">Producent</dt><dd class="col-span-2">{{ $cable->manufacturer?->MCF_NAME ?? '-' }}</dd>
|
||||
<dt class="text-on-surface-variant">Opis</dt><dd class="col-span-2">{!! $cable->EQU_DESCRIPTION ? nl2br(e($cable->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 ($inStock)
|
||||
<p class="mt-4 font-body-sm text-body-sm text-on-surface-variant/80">Kabel jest na stanie magazynowym - 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="{{ $cable->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>
|
||||
@@ -0,0 +1,38 @@
|
||||
<x-app-layout :active="'engines.index'" title="SoftSPM 2 - Nowy silnik">
|
||||
<div class="max-w-2xl space-y-stack-gap">
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('engines.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">Nowy silnik</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('engines.store') }}"
|
||||
class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card p-card-inner-padding space-y-4">
|
||||
@csrf
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<x-well-field name="name" label="Typ silnika" :value="old('name')" required class="sm:col-span-2" />
|
||||
<x-well-select name="manufacturer_id" label="Producent" :options="$manufacturers" :selected="old('manufacturer_id')" required placeholder="- wybierz -" class="sm:col-span-2" />
|
||||
|
||||
<x-well-field name="power" label="Moc [kW]" :value="old('power')" required />
|
||||
<x-well-field name="voltage" label="Napięcie [V]" :value="old('voltage')" required />
|
||||
<x-well-field name="current" label="Prąd [A]" :value="old('current')" />
|
||||
<x-well-field name="eta" label="Sprawność η [-]" :value="old('eta')" />
|
||||
<x-well-field name="cos_fi" label="Wsp. mocy cos(φ) [-]" :value="old('cos_fi')" />
|
||||
<x-well-field name="rpm" label="Obroty [1/min]" :value="old('rpm')" />
|
||||
<x-well-field name="length" label="Długość [mm]" :value="old('length')" />
|
||||
<x-well-field name="weight" label="Masa [kg]" :value="old('weight')" />
|
||||
<x-well-field name="diameter" label="Średnica [mm]" :value="old('diameter')" />
|
||||
<x-well-field name="wires" label="Wprowadzenie przewodów" :value="old('wires')" />
|
||||
</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('engines.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,39 @@
|
||||
<x-app-layout :active="'engines.index'" title="SoftSPM 2 - Edycja silnika">
|
||||
<div class="max-w-2xl space-y-stack-gap">
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('engines.show', $engine) }}" 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 silnika: {{ $engine->ENG_NAME }}</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('engines.update', $engine) }}"
|
||||
class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card p-card-inner-padding space-y-4">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<x-well-field name="name" label="Typ silnika" :value="$engine->ENG_NAME" required class="sm:col-span-2" />
|
||||
<x-well-select name="manufacturer_id" label="Producent" :options="$manufacturers" :selected="$engine->ENG_MCF_ID" required class="sm:col-span-2" />
|
||||
|
||||
<x-well-field name="power" label="Moc [kW]" :value="$engine->ENG_POWER" required />
|
||||
<x-well-field name="voltage" label="Napięcie [V]" :value="$engine->ENG_VOLTAGE" required />
|
||||
<x-well-field name="current" label="Prąd [A]" :value="$engine->ENG_ELECTRICITY" />
|
||||
<x-well-field name="eta" label="Sprawność η [-]" :value="$engine->ENG_ETA" />
|
||||
<x-well-field name="cos_fi" label="Wsp. mocy cos(φ) [-]" :value="$engine->ENG_COS_FI" />
|
||||
<x-well-field name="rpm" label="Obroty [1/min]" :value="$engine->ENG_RPM" />
|
||||
<x-well-field name="length" label="Długość [mm]" :value="$engine->ENG_LENGTH" />
|
||||
<x-well-field name="weight" label="Masa [kg]" :value="$engine->ENG_WEIGHT" />
|
||||
<x-well-field name="diameter" label="Średnica [mm]" :value="$engine->ENG_DIAMETER" />
|
||||
<x-well-field name="wires" label="Wprowadzenie przewodów" :value="$engine->ENG_WIRES" />
|
||||
</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('engines.show', $engine) }}" 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,84 @@
|
||||
<x-app-layout :active="'engines.index'" title="SoftSPM 2 - Katalog silników">
|
||||
<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 silników</h1>
|
||||
<p class="font-body-sm text-body-sm text-on-surface-variant mt-1">Wyników: <span class="font-data-label">{{ $engines->total() }}</span></p>
|
||||
</div>
|
||||
<a href="{{ route('engines.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 silnik
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="GET" action="{{ route('engines.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="by" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Kryterium</label>
|
||||
<select id="by" name="by" class="bg-surface-container-low border border-outline-variant px-3 py-2 rounded font-data-label text-on-surface">
|
||||
<option value="name" {{ $by === 'name' ? 'selected' : '' }}>Typ silnika</option>
|
||||
<option value="power" {{ $by === 'power' ? 'selected' : '' }}>Moc</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<label for="q" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Szukaj</label>
|
||||
<input id="q" name="q" type="text" value="{{ $q }}" placeholder="typ lub wartość Moc"
|
||||
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>
|
||||
|
||||
<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">Typ silnika</x-sortable-th>
|
||||
<x-sortable-th column="voltage" align="right">Napięcie [V]</x-sortable-th>
|
||||
<x-sortable-th column="power" align="right">Moc [kW]</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 ($engines as $engine)
|
||||
<tr class="hover:bg-surface-container transition-colors">
|
||||
<td class="px-4 py-3 font-data-label text-on-surface-variant">{{ $engines->firstItem() + $loop->index }}.</td>
|
||||
<td class="px-4 py-3 font-semibold text-on-surface">
|
||||
<a href="{{ route('engines.show', $engine) }}" class="hover:text-primary">{{ $engine->ENG_NAME }}</a>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-right font-data-label">{{ $engine->ENG_VOLTAGE }}</td>
|
||||
<td class="px-4 py-3 text-right font-data-label">{{ $engine->ENG_POWER }}</td>
|
||||
<td class="px-4 py-3">{{ $engine->manufacturer?->MCF_NAME }}</td>
|
||||
<td class="px-4 py-3 text-right whitespace-nowrap">
|
||||
<a href="{{ route('engines.show', $engine) }}" 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('engines.edit', $engine) }}" class="font-body-sm text-body-sm text-secondary hover:underline">Edytuj</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="px-4 py-8 text-center font-body-md text-body-md text-on-surface-variant">Brak silników dla wybranych kryteriów.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>{{ $engines->links() }}</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,75 @@
|
||||
<x-app-layout :active="'engines.index'" title="SoftSPM 2 - {{ $engine->ENG_NAME }}">
|
||||
@php $fmt = fn ($v, $d = 2) => $v === null ? '-' : number_format(round((float) $v, $d), $d, ',', ' '); @endphp
|
||||
|
||||
<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 flex-wrap items-center justify-between gap-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('engines.index') }}" class="font-body-sm text-body-sm text-on-surface-variant hover:text-primary">← Katalog silników</a>
|
||||
<h1 class="font-headline-lg text-headline-lg text-on-surface">{{ $engine->ENG_NAME }}</h1>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('engines.edit', $engine) }}"
|
||||
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>
|
||||
<x-confirm-delete :action="route('engines.destroy', $engine)"
|
||||
message="Czy na pewno usunąć silnik „{{ $engine->ENG_NAME }}”? Tej operacji nie można cofnąć." />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-stack-gap">
|
||||
{{-- Dane techniczne --}}
|
||||
<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 techniczne silnika</h2>
|
||||
<dl class="grid grid-cols-2 gap-x-4 gap-y-2 font-body-md text-body-md">
|
||||
<dt class="text-on-surface-variant">Typ silnika</dt><dd class="font-semibold">{{ $engine->ENG_NAME }}</dd>
|
||||
<dt class="text-on-surface-variant">Producent</dt><dd>{{ $engine->manufacturer?->MCF_NAME ?? '-' }}</dd>
|
||||
<dt class="col-span-2 border-t border-outline-variant/30 mt-1 pt-1"></dt>
|
||||
<dt class="text-on-surface-variant">Napięcie [V]</dt><dd class="font-data-label">{{ $engine->ENG_VOLTAGE ?? '-' }}</dd>
|
||||
<dt class="text-on-surface-variant">Moc [kW]</dt><dd class="font-data-label">{{ $fmt($engine->ENG_POWER) }}</dd>
|
||||
<dt class="text-on-surface-variant">Prąd [A]</dt><dd class="font-data-label">{{ $fmt($engine->ENG_ELECTRICITY) }}</dd>
|
||||
<dt class="text-on-surface-variant">Sprawność η [-]</dt><dd class="font-data-label">{{ $fmt($engine->ENG_ETA) }}</dd>
|
||||
<dt class="text-on-surface-variant">Wsp. mocy cos(φ) [-]</dt><dd class="font-data-label">{{ $fmt($engine->ENG_COS_FI) }}</dd>
|
||||
<dt class="text-on-surface-variant">Obroty [1/min]</dt><dd class="font-data-label">{{ $fmt($engine->ENG_RPM) }}</dd>
|
||||
<dt class="col-span-2 border-t border-outline-variant/30 mt-1 pt-1"></dt>
|
||||
<dt class="text-on-surface-variant">Długość [mm]</dt><dd class="font-data-label">{{ $fmt($engine->ENG_LENGTH) }}</dd>
|
||||
<dt class="text-on-surface-variant">Średnica [mm]</dt><dd class="font-data-label">{{ $fmt($engine->ENG_DIAMETER) }}</dd>
|
||||
<dt class="text-on-surface-variant">Masa [kg]</dt><dd class="font-data-label">{{ $fmt($engine->ENG_WEIGHT) }}</dd>
|
||||
<dt class="text-on-surface-variant">Wprow. przewodów</dt><dd>{{ $engine->ENG_WIRES ?? '-' }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{-- Lista pomp dla silnika --}}
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="px-card-inner-padding pt-card-inner-padding">
|
||||
<h2 class="font-title-sm text-title-sm text-tertiary mb-2">Lista pomp dla silnika</h2>
|
||||
</div>
|
||||
<table class="w-full text-left">
|
||||
<thead class="bg-secondary/10 border-y border-outline-variant/40">
|
||||
<tr class="font-data-label text-data-label text-on-surface-variant tracking-wide">
|
||||
<th class="px-4 py-2">Typ pompy</th>
|
||||
<th class="px-4 py-2 text-right">Q<sub>n</sub> [m³/h]</th>
|
||||
<th class="px-4 py-2 text-right">H<sub>p</sub> [m]</th>
|
||||
<th class="px-4 py-2">Producent</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-outline-variant/30 font-body-md text-body-md">
|
||||
@forelse ($relatedPumps as $p)
|
||||
<tr>
|
||||
<td class="px-4 py-1.5">{{ $p->PUP_NAME }}</td>
|
||||
<td class="px-4 py-1.5 text-right font-data-label">{{ number_format(round((float) $p->q_n, 3), 3, ',', ' ') }}</td>
|
||||
<td class="px-4 py-1.5 text-right font-data-label">{{ $p->PUP_H_P }}</td>
|
||||
<td class="px-4 py-1.5">{{ $p->MCF_NAME }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="4" class="px-4 py-4 text-center font-body-sm text-on-surface-variant">Brak powiązanych pomp.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,52 @@
|
||||
<x-app-layout :active="'piping.index'" title="SoftSPM 2 - Nowa pozycja orurowania">
|
||||
<div class="max-w-2xl space-y-stack-gap">
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('piping.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">Nowa pozycja - orurowanie / armatura</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('piping.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="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<x-well-field name="diameter" label="Średnica [mm]" :value="old('diameter')" required />
|
||||
<x-well-field name="length" label="Długość [m] / Ciśnienie [bar]" :value="old('length')" />
|
||||
</div>
|
||||
|
||||
<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('piping.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,61 @@
|
||||
<x-app-layout :active="'piping.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('piping.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 pozycji - orurowanie / armatura</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('piping.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="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<x-well-field name="diameter" label="Średnica [mm]" :value="old('diameter', $item->EQU_PARAM_01)" required />
|
||||
<x-well-field name="length" label="Długość [m] / Ciśnienie [bar]" :value="old('length', $item->EQU_PARAM_02)" />
|
||||
</div>
|
||||
|
||||
<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('piping.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,79 @@
|
||||
<x-app-layout :active="'piping.index'" title="SoftSPM 2 - Katalog orurowania i armatury">
|
||||
<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 orurowania i armatury</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('piping.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 pozycję
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{-- Wyszukiwarka --}}
|
||||
<form method="GET" action="{{ route('piping.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">Typ orurowania - armatury</label>
|
||||
<input id="q" name="q" type="text" value="{{ $q }}" placeholder="nazwa"
|
||||
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="diameter" align="right">Średnica [mm]</x-sortable-th>
|
||||
<x-sortable-th column="length">Długość [m] / Ciśnienie [bar]</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('piping.show', $item) }}" class="hover:text-primary">{{ $item->EQU_NAME }}</a>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-right font-data-label">{{ $item->EQU_PARAM_01 }}</td>
|
||||
<td class="px-4 py-3 font-data-label">{{ $item->EQU_PARAM_02 }}</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('piping.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('piping.edit', $item) }}" class="font-body-sm text-body-sm text-secondary hover:underline">Edytuj</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="px-4 py-8 text-center font-body-md text-body-md text-on-surface-variant">Brak pozycji dla wybranych kryteriów.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>{{ $items->links() }}</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,63 @@
|
||||
<x-app-layout :active="'piping.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('piping.index') }}" class="font-body-sm text-body-sm text-on-surface-variant hover:text-primary">← Katalog orurowania i armatury</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('piping.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 ($inStock)
|
||||
<x-confirm-delete :action="route('piping.destroy', $item)"
|
||||
message="Czy na pewno usunąć pozycję „{{ $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">Średnica [mm]</dt><dd class="col-span-2 font-data-label">{{ $item->EQU_PARAM_01 ?: '-' }}</dd>
|
||||
<dt class="text-on-surface-variant">Długość [m] / Ciśnienie [bar]</dt><dd class="col-span-2 font-data-label">{{ $item->EQU_PARAM_02 ?: '-' }}</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 ($inStock)
|
||||
<p class="mt-4 font-body-sm text-body-sm text-on-surface-variant/80">Pozycja jest na stanie magazynowym - 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>
|
||||
@@ -0,0 +1,83 @@
|
||||
<x-app-layout :active="'pumps.index'" title="SoftSPM 2 - Nowa pompa">
|
||||
@php $factors = [0, 0.25, 0.55, 0.7, 1.0, 1.22, 1.35, 1.42]; @endphp
|
||||
|
||||
<div class="max-w-3xl space-y-stack-gap">
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('pumps.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">Nowa pompa</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('pumps.store') }}"
|
||||
class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card p-card-inner-padding space-y-6">
|
||||
@csrf
|
||||
|
||||
{{-- Dane podstawowe --}}
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<x-well-field name="name" label="Typ pompy" :value="old('name')" required class="sm:col-span-2" />
|
||||
<x-well-select name="manufacturer_id" label="Producent" :options="$manufacturers" :selected="old('manufacturer_id')" required placeholder="- wybierz -" class="sm:col-span-2" />
|
||||
<x-well-field name="q_n" label="Qₙ [m³/h]" :value="old('q_n')" required />
|
||||
<x-well-field name="length" label="Długość [mm]" :value="old('length')" />
|
||||
<x-well-field name="diameter" label="Średnica [mm]" :value="old('diameter')" />
|
||||
<x-well-field name="weight" label="Masa [kg]" :value="old('weight')" />
|
||||
</div>
|
||||
|
||||
{{-- 8 punktów krzywych - Q liczone z Qₙ, użytkownik podaje H i η --}}
|
||||
<div>
|
||||
<h2 class="font-title-sm text-title-sm text-tertiary mb-1">Parametry energetyczne (8 punktów)</h2>
|
||||
<p class="font-body-sm text-body-sm text-on-surface-variant mb-2">
|
||||
Wartości Q wyliczane automatycznie z Qₙ. Podaj H i η dla każdego punktu.
|
||||
</p>
|
||||
<table class="w-full text-left border border-outline-variant/40 rounded-lg overflow-hidden">
|
||||
<thead class="bg-secondary/10">
|
||||
<tr class="font-data-label text-data-label text-on-surface-variant tracking-wide">
|
||||
<th class="px-3 py-2 w-10">Lp.</th>
|
||||
<th class="px-3 py-2 text-right">Q [m³/h]</th>
|
||||
<th class="px-3 py-2">H [m]</th>
|
||||
<th class="px-3 py-2">η [-]</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-outline-variant/30">
|
||||
@foreach ($factors as $i => $f)
|
||||
<tr>
|
||||
<td class="px-3 py-1.5 font-data-label text-on-surface-variant">{{ $i + 1 }}.</td>
|
||||
<td class="px-3 py-1.5 text-right font-data-label" data-qcell>0,000</td>
|
||||
<td class="px-3 py-1.5">
|
||||
<input type="text" name="h[{{ $i }}]" value="{{ old('h.'.$i) }}" required
|
||||
class="w-28 bg-surface-container-low border border-outline-variant px-2 py-1 rounded font-data-label text-on-surface">
|
||||
</td>
|
||||
<td class="px-3 py-1.5">
|
||||
<input type="text" name="eta[{{ $i }}]" value="{{ old('eta.'.$i) }}" required
|
||||
class="w-28 bg-surface-container-low border border-outline-variant px-2 py-1 rounded font-data-label text-on-surface">
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-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('pumps.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>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const factors = @json($factors);
|
||||
const qn = document.getElementById('q_n');
|
||||
const cells = document.querySelectorAll('[data-qcell]');
|
||||
function recalc() {
|
||||
const v = parseFloat((qn.value || '').replace(',', '.')) || 0;
|
||||
cells.forEach((el, i) => { el.textContent = (factors[i] * v).toFixed(3).replace('.', ','); });
|
||||
}
|
||||
qn.addEventListener('input', recalc);
|
||||
recalc();
|
||||
})();
|
||||
</script>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,88 @@
|
||||
<x-app-layout :active="'pumps.index'" title="SoftSPM 2 - Edycja pompy">
|
||||
<div class="max-w-4xl space-y-stack-gap">
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('pumps.show', $pump) }}" 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 pompy: {{ $pump->PUP_NAME }}</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('pumps.update', $pump) }}"
|
||||
class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card p-card-inner-padding space-y-6">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
{{-- Dane podstawowe --}}
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<x-well-field name="name" label="Typ pompy" :value="$pump->PUP_NAME" required class="sm:col-span-2" />
|
||||
<x-well-select name="manufacturer_id" label="Producent" :options="$manufacturers" :selected="$pump->PUP_MCF_ID" required class="sm:col-span-2" />
|
||||
<x-well-field name="_qn" label="Qₙ [m³/h]" :value="$qn" readonly />
|
||||
<x-well-field name="length" label="Długość [mm]" :value="$pump->PUP_PUMP_LENGTH" />
|
||||
<x-well-field name="diameter" label="Średnica [mm]" :value="$pump->PUP_DIAMETER" />
|
||||
<x-well-field name="weight" label="Masa [kg]" :value="$pump->PUP_WEIGHT" />
|
||||
</div>
|
||||
|
||||
{{-- Krzywe --}}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{{-- Q-H --}}
|
||||
<div>
|
||||
<h2 class="font-title-sm text-title-sm text-tertiary mb-2">Wysokość podnoszenia</h2>
|
||||
<table class="w-full text-left border border-outline-variant/40 rounded-lg overflow-hidden">
|
||||
<thead class="bg-secondary/10">
|
||||
<tr class="font-data-label text-data-label text-on-surface-variant tracking-wide">
|
||||
<th class="px-3 py-2 text-right">Q [m³/h]</th>
|
||||
<th class="px-3 py-2">H [m]</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-outline-variant/30">
|
||||
@foreach ($h as $pt)
|
||||
<tr>
|
||||
<td class="px-3 py-1.5 text-right font-data-label text-on-surface-variant">{{ $pt->x }}</td>
|
||||
<td class="px-3 py-1.5">
|
||||
<input type="text" name="h[{{ $pt->id }}]" value="{{ $pt->y }}"
|
||||
class="w-28 bg-surface-container-low border border-outline-variant px-2 py-1 rounded font-data-label text-on-surface">
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{-- Q-ETA --}}
|
||||
<div>
|
||||
<h2 class="font-title-sm text-title-sm text-tertiary mb-2">Sprawność</h2>
|
||||
<table class="w-full text-left border border-outline-variant/40 rounded-lg overflow-hidden">
|
||||
<thead class="bg-secondary/10">
|
||||
<tr class="font-data-label text-data-label text-on-surface-variant tracking-wide">
|
||||
<th class="px-3 py-2 text-right">Q [m³/h]</th>
|
||||
<th class="px-3 py-2">η [-]</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-outline-variant/30">
|
||||
@foreach ($eta as $pt)
|
||||
<tr>
|
||||
<td class="px-3 py-1.5 text-right font-data-label text-on-surface-variant">{{ $pt->x }}</td>
|
||||
<td class="px-3 py-1.5">
|
||||
<input type="text" name="eta[{{ $pt->id }}]" value="{{ $pt->y }}"
|
||||
class="w-28 bg-surface-container-low border border-outline-variant px-2 py-1 rounded font-data-label text-on-surface">
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-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('pumps.show', $pump) }}" 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,95 @@
|
||||
<x-app-layout :active="'pumps.index'" title="SoftSPM 2 - Katalog pomp">
|
||||
<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 pomp</h1>
|
||||
<p class="font-body-sm text-body-sm text-on-surface-variant mt-1">
|
||||
Wyników: <span class="font-data-label">{{ $pumps->total() }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<a href="{{ route('pumps.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 pompę
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{-- Wyszukiwarka --}}
|
||||
<form method="GET" action="{{ route('pumps.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="by" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Kryterium</label>
|
||||
<select id="by" name="by" class="bg-surface-container-low border border-outline-variant px-3 py-2 rounded font-data-label text-on-surface">
|
||||
<option value="name" {{ $by === 'name' ? 'selected' : '' }}>Typ pompy</option>
|
||||
<option value="flow" {{ $by === 'flow' ? 'selected' : '' }}>Wydajność Q</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<label for="q" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Szukaj</label>
|
||||
<input id="q" name="q" type="text" value="{{ $q }}" placeholder="typ lub wartość Q"
|
||||
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">Typ pompy</x-sortable-th>
|
||||
<x-sortable-th column="flow" align="right">Q<sub>n</sub> [m³/h]</x-sortable-th>
|
||||
<x-sortable-th column="hp" align="right">H<sub>p</sub> [m]</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 ($pumps as $pump)
|
||||
<tr class="hover:bg-surface-container transition-colors">
|
||||
<td class="px-4 py-3 font-data-label text-on-surface-variant">{{ $pumps->firstItem() + $loop->index }}.</td>
|
||||
<td class="px-4 py-3 font-semibold text-on-surface">
|
||||
<a href="{{ route('pumps.show', $pump) }}" class="hover:text-primary">{{ $pump->PUP_NAME }}</a>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-right font-data-label">{{ number_format(round((float) $pump->PUP_Q_N, 3), 3, ',', ' ') }}</td>
|
||||
<td class="px-4 py-3 text-right font-data-label">{{ $pump->PUP_H_P }}</td>
|
||||
<td class="px-4 py-3">{{ $pump->manufacturer?->MCF_NAME }}</td>
|
||||
<td class="px-4 py-3 text-right whitespace-nowrap">
|
||||
<a href="{{ route('pumps.show', $pump) }}" 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('pumps.edit', $pump) }}" class="font-body-sm text-body-sm text-secondary hover:underline">Edytuj</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="px-4 py-8 text-center font-body-md text-body-md text-on-surface-variant">
|
||||
Brak pomp dla wybranych kryteriów.
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>{{ $pumps->links() }}</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,152 @@
|
||||
<x-app-layout :active="'pumps.index'" title="SoftSPM 2 - {{ $pump->PUP_NAME }}">
|
||||
@php
|
||||
$fmt = fn ($v, $d = 3) => $v === null ? '-' : number_format(round((float) $v, $d), $d, ',', ' ');
|
||||
@endphp
|
||||
|
||||
<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
|
||||
|
||||
{{-- Nagłówek + akcje --}}
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('pumps.index') }}" class="font-body-sm text-body-sm text-on-surface-variant hover:text-primary">← Katalog pomp</a>
|
||||
<h1 class="font-headline-lg text-headline-lg text-on-surface">{{ $pump->PUP_NAME }}</h1>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('pumps.edit', $pump) }}"
|
||||
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>
|
||||
<x-confirm-delete :action="route('pumps.destroy', $pump)"
|
||||
message="Czy na pewno usunąć pompę „{{ $pump->PUP_NAME }}”? Tej operacji nie można cofnąć." />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-stack-gap">
|
||||
|
||||
{{-- LEWA: dane techniczne --}}
|
||||
<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 techniczne pompy</h2>
|
||||
<dl class="grid grid-cols-2 gap-x-4 gap-y-2 font-body-md text-body-md">
|
||||
<dt class="text-on-surface-variant">Typ pompy</dt><dd class="font-semibold">{{ $pump->PUP_NAME }}</dd>
|
||||
<dt class="text-on-surface-variant">Producent</dt><dd>{{ $pump->manufacturer?->MCF_NAME ?? '-' }}</dd>
|
||||
<dt class="col-span-2 border-t border-outline-variant/30 mt-1 pt-1"></dt>
|
||||
<dt class="text-on-surface-variant">Q<sub>n</sub> [m³/h]</dt><dd class="font-data-label">{{ $fmt($pump->PUP_Q_N) }}</dd>
|
||||
<dt class="text-on-surface-variant">Q<sub>min</sub> [m³/h]</dt><dd class="font-data-label">{{ $fmt($pump->PUP_Q_MIN) }}</dd>
|
||||
<dt class="text-on-surface-variant">Q<sub>max</sub> [m³/h]</dt><dd class="font-data-label">{{ $fmt($pump->PUP_Q_MAX) }}</dd>
|
||||
<dt class="text-on-surface-variant">H<sub>p</sub> [m]</dt><dd class="font-data-label">{{ $fmt($pump->PUP_H_P, 2) }}</dd>
|
||||
<dt class="text-on-surface-variant">H<sub>min</sub> [m]</dt><dd class="font-data-label">{{ $fmt($pump->PUP_H_MIN, 2) }}</dd>
|
||||
<dt class="text-on-surface-variant">H<sub>max</sub> [m]</dt><dd class="font-data-label">{{ $fmt($pump->PUP_H_MAX, 2) }}</dd>
|
||||
<dt class="text-on-surface-variant">η<sub>max</sub> [-]</dt><dd class="font-data-label">{{ $fmt($pump->PUP_ETA_MAX, 2) }}</dd>
|
||||
<dt class="col-span-2 border-t border-outline-variant/30 mt-1 pt-1"></dt>
|
||||
<dt class="text-on-surface-variant">Długość [mm]</dt><dd class="font-data-label">{{ $fmt($pump->PUP_PUMP_LENGTH, 2) }}</dd>
|
||||
<dt class="text-on-surface-variant">Średnica [mm]</dt><dd class="font-data-label">{{ $fmt($pump->PUP_DIAMETER, 2) }}</dd>
|
||||
<dt class="text-on-surface-variant">Masa [kg]</dt><dd class="font-data-label">{{ $fmt($pump->PUP_WEIGHT, 2) }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{-- PRAWA: wykresy --}}
|
||||
<div class="space-y-stack-gap">
|
||||
<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-2">Wysokość podnoszenia <span class="font-body-sm text-on-surface-variant">H = f(Q)</span></h2>
|
||||
@if ($hDrawable)
|
||||
<x-pump-curve :plot="$hPlot" id="hchart" :interactive="true" :initial-q="$q"
|
||||
:eta-coeffs="data_get($etaPlot, 'coeffs', [])"
|
||||
q-input="#q" h-output="#rv-h" eta-output="#rv-eta"
|
||||
x-label="Q [m³/h]" y-label="H [m]" />
|
||||
@else
|
||||
<p class="font-body-sm text-body-sm text-on-surface-variant py-6 text-center">Brak parametrów energetycznych dla pompy.</p>
|
||||
@endif
|
||||
</div>
|
||||
<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-2">Sprawność <span class="font-body-sm text-on-surface-variant">η = f(Q)</span></h2>
|
||||
@if ($etaDrawable)
|
||||
<x-pump-curve :plot="$etaPlot" x-label="Q [m³/h]" y-label="η [-]" />
|
||||
@else
|
||||
<p class="font-body-sm text-body-sm text-on-surface-variant py-6 text-center">Brak parametrów energetycznych dla pompy.</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-stack-gap">
|
||||
{{-- Parametry energetyczne (punkty krzywych) --}}
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="px-card-inner-padding pt-card-inner-padding">
|
||||
<h2 class="font-title-sm text-title-sm text-tertiary mb-2">Parametry energetyczne</h2>
|
||||
</div>
|
||||
<table class="w-full text-left">
|
||||
<thead class="bg-secondary/10 border-y border-outline-variant/40">
|
||||
<tr class="font-data-label text-data-label text-on-surface-variant tracking-wide">
|
||||
<th class="px-4 py-2 w-10">Lp.</th>
|
||||
<th class="px-4 py-2 text-right">Q [m³/h]</th>
|
||||
<th class="px-4 py-2 text-right">H [m]</th>
|
||||
<th class="px-4 py-2 text-right">η [-]</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-outline-variant/30 font-data-label">
|
||||
@foreach ($h as $i => $pt)
|
||||
<tr>
|
||||
<td class="px-4 py-1.5 text-on-surface-variant">{{ $i + 1 }}.</td>
|
||||
<td class="px-4 py-1.5 text-right">{{ $pt->x }}</td>
|
||||
<td class="px-4 py-1.5 text-right">{{ $pt->y }}</td>
|
||||
<td class="px-4 py-1.5 text-right">{{ $eta[$i]->y ?? '-' }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{-- Przelicz dla zadanego Q + lista silników --}}
|
||||
<div class="space-y-stack-gap">
|
||||
<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">Parametry energetyczne na zadane Q</h2>
|
||||
<form method="GET" action="{{ route('pumps.show', $pump) }}" 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">Dla Q [m³/h]</label>
|
||||
<input id="q" name="q" type="text" value="{{ number_format($q, 3, ',', '') }}"
|
||||
class="w-32 bg-surface-container-low border border-outline-variant px-3 py-2 rounded font-data-label text-on-surface">
|
||||
</div>
|
||||
<button type="submit" class="bg-secondary text-on-secondary px-4 py-2 rounded-full font-title-sm text-title-sm hover:opacity-90 active:scale-95">Przelicz</button>
|
||||
@if ($outOfRange)
|
||||
<span class="font-body-sm text-body-sm text-error">poza zakresem</span>
|
||||
@endif
|
||||
</form>
|
||||
<dl class="grid grid-cols-2 gap-2 mt-3 font-body-md text-body-md">
|
||||
<dt class="text-on-surface-variant">Wartość H [m]</dt><dd id="rv-h" class="font-data-label font-semibold">{{ $hAtQ === null ? '-' : number_format(round($hAtQ, 2), 2, ',', ' ') }}</dd>
|
||||
<dt class="text-on-surface-variant">Wartość η [-]</dt><dd id="rv-eta" class="font-data-label font-semibold">{{ $etaAtQ === null ? '-' : number_format(round($etaAtQ, 2), 2, ',', ' ') }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="px-card-inner-padding pt-card-inner-padding">
|
||||
<h2 class="font-title-sm text-title-sm text-tertiary mb-2">Lista silników dla pompy</h2>
|
||||
</div>
|
||||
<table class="w-full text-left">
|
||||
<thead class="bg-secondary/10 border-y border-outline-variant/40">
|
||||
<tr class="font-data-label text-data-label text-on-surface-variant tracking-wide">
|
||||
<th class="px-4 py-2">Typ silnika</th>
|
||||
<th class="px-4 py-2 text-right">Napięcie [V]</th>
|
||||
<th class="px-4 py-2 text-right">Moc [kW]</th>
|
||||
<th class="px-4 py-2">Producent</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-outline-variant/30 font-body-md text-body-md">
|
||||
@forelse ($relatedEngines as $e)
|
||||
<tr>
|
||||
<td class="px-4 py-1.5">{{ $e->ENG_NAME }}</td>
|
||||
<td class="px-4 py-1.5 text-right font-data-label">{{ $e->ENG_VOLTAGE }}</td>
|
||||
<td class="px-4 py-1.5 text-right font-data-label">{{ $e->ENG_POWER }}</td>
|
||||
<td class="px-4 py-1.5">{{ $e->MCF_NAME }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="4" class="px-4 py-4 text-center font-body-sm text-on-surface-variant">Brak powiązanych silników.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,295 @@
|
||||
@props([
|
||||
'active' => null, // nazwa trasy aktywnej pozycji (np. 'wells.index')
|
||||
'section' => null, // klucz sekcji (gdy strona nie jest pozycją menu, np. landing sekcji)
|
||||
'title' => 'SoftSPM 2',
|
||||
])
|
||||
|
||||
@php
|
||||
$sections = config('navigation');
|
||||
|
||||
// Ustal aktywną sekcję: jawny prop > sekcja zawierająca aktywną pozycję > pierwsza.
|
||||
$activeSectionKey = $section;
|
||||
if (! $activeSectionKey && $active) {
|
||||
foreach ($sections as $s) {
|
||||
foreach ($s['items'] as $it) {
|
||||
if (($it['route'] ?? null) === $active) {
|
||||
$activeSectionKey = $s['key'];
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$activeSectionKey = $activeSectionKey ?? ($sections[0]['key'] ?? null);
|
||||
$activeSection = collect($sections)->firstWhere('key', $activeSectionKey);
|
||||
|
||||
// Motyw z cookie (serwer ustawia klasę przy renderze - brak migotania).
|
||||
// Czytamy SUROWE $_COOKIE - cookie 'theme' ustawia JS bez szyfrowania, więc
|
||||
// request()->cookie() (przez EncryptCookies) by go nie odczytał.
|
||||
$theme = ($_COOKIE['theme'] ?? null) === 'dark' ? 'dark' : 'light';
|
||||
|
||||
// Inicjały użytkownika do awatara w menu.
|
||||
$uname = auth()->user()?->USR_USERNAME ?? '?';
|
||||
$unameParts = preg_split('/[^\p{L}\p{N}]+/u', $uname, -1, PREG_SPLIT_NO_EMPTY) ?: [$uname];
|
||||
$initials = mb_strtoupper(count($unameParts) >= 2
|
||||
? mb_substr($unameParts[0], 0, 1).mb_substr($unameParts[1], 0, 1)
|
||||
: mb_substr($uname, 0, 2));
|
||||
|
||||
$languages = ['pl' => 'PL', 'en' => 'EN', 'de' => 'DE'];
|
||||
@endphp
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html class="{{ $theme }}" lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ $title }}</title>
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
</head>
|
||||
<body class="bg-surface text-on-surface min-h-screen">
|
||||
|
||||
{{-- ===== Górny pasek: SEKCJE (zakres) ===== --}}
|
||||
<header class="sticky top-0 z-30 bg-surface-container-lowest border-b border-outline-variant/40">
|
||||
<div class="flex items-center justify-between h-16 px-gutter">
|
||||
{{-- Logo + sekcje razem, wyrównane do lewej --}}
|
||||
<div class="flex items-center gap-10 min-w-0">
|
||||
<a href="{{ route('dashboard') }}" class="shrink-0">
|
||||
<img src="{{ asset('images/logo.png') }}" alt="SoftSPM 2" class="h-9 w-auto">
|
||||
</a>
|
||||
|
||||
<nav class="hidden md:flex items-center gap-6 font-body-md text-body-md overflow-x-auto">
|
||||
@foreach ($sections as $s)
|
||||
@php
|
||||
$landing = isset($s['route']) && Route::has($s['route'])
|
||||
? route($s['route'])
|
||||
: route('section', $s['key']);
|
||||
@endphp
|
||||
<a href="{{ $landing }}"
|
||||
class="whitespace-nowrap py-1 border-b-2 transition-colors hover:text-primary
|
||||
{{ $activeSectionKey === $s['key'] ? 'text-primary font-semibold border-primary' : 'text-on-surface border-transparent' }}">
|
||||
{{ $s['label'] }}
|
||||
</a>
|
||||
@endforeach
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 shrink-0">
|
||||
{{-- Przełącznik motywu jasny/ciemny --}}
|
||||
<button type="button" id="theme-toggle" title="Przełącz motyw jasny/ciemny" aria-label="Przełącz motyw jasny/ciemny"
|
||||
class="p-2 rounded-full text-on-surface-variant hover:bg-surface-container hover:text-primary transition-colors">
|
||||
{{-- W jasnym pokazuje księżyc (przejście w ciemny) --}}
|
||||
<svg class="w-5 h-5 dark:hidden" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9z"/>
|
||||
</svg>
|
||||
{{-- W ciemnym pokazuje słońce (przejście w jasny) --}}
|
||||
<svg class="w-5 h-5 hidden dark:block" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="4"/>
|
||||
<path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{{-- Menu użytkownika: awatar -> rozwijane (język + wyloguj) --}}
|
||||
<div class="relative" id="user-menu">
|
||||
<button type="button" id="user-menu-button" aria-haspopup="true" aria-expanded="false"
|
||||
class="flex items-center gap-2 rounded-full hover:opacity-90 transition-opacity">
|
||||
<span class="grid place-items-center w-9 h-9 rounded-full bg-primary text-on-primary font-data-label text-data-label">{{ $initials }}</span>
|
||||
</button>
|
||||
|
||||
<div id="user-menu-panel"
|
||||
class="hidden absolute right-0 mt-2 w-60 bg-surface-container-lowest border border-outline-variant/40 rounded-xl shadow-card py-2 z-40">
|
||||
<div class="px-4 py-2 border-b border-outline-variant/30">
|
||||
<p class="font-body-sm text-body-sm text-on-surface-variant">Zalogowano jako</p>
|
||||
<p class="font-data-label text-data-label text-on-surface truncate">{{ $uname }}</p>
|
||||
</div>
|
||||
|
||||
<div class="px-4 py-3">
|
||||
<p class="font-data-label text-data-label text-on-surface-variant/70 uppercase tracking-wider mb-2">Język</p>
|
||||
<div class="flex gap-1.5">
|
||||
@foreach ($languages as $code => $label)
|
||||
<a href="{{ route('locale.set', $code) }}"
|
||||
class="px-3 py-1 rounded-md font-data-label text-data-label transition-colors
|
||||
{{ app()->getLocale() === $code ? 'bg-secondary-container text-on-secondary-container font-semibold' : 'text-on-surface-variant hover:bg-surface-container' }}">
|
||||
{{ $label }}
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border-t border-outline-variant/30 mt-1 pt-1">
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
class="w-full text-left px-4 py-2 font-body-md text-body-md text-on-surface hover:bg-surface-container transition-colors">
|
||||
Wyloguj
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="flex min-h-[calc(100vh-4rem)]">
|
||||
{{-- ===== Lewy sidebar: POZYCJE aktywnej sekcji ===== --}}
|
||||
<aside class="hidden lg:block w-72 shrink-0 bg-surface-container-lowest border-r border-outline-variant/40 p-gutter">
|
||||
<p class="font-data-label text-data-label text-on-surface-variant uppercase tracking-wider mb-4">
|
||||
{{ $activeSection['label'] ?? 'Nawigacja' }}
|
||||
</p>
|
||||
<nav class="space-y-1">
|
||||
@forelse ($activeSection['items'] ?? [] as $it)
|
||||
@php
|
||||
$r = $it['route'] ?? null;
|
||||
$url = ($r && Route::has($r)) ? route($r) : '#';
|
||||
$isActive = $r && $r === $active;
|
||||
@endphp
|
||||
<a href="{{ $url }}"
|
||||
class="flex items-center gap-3 px-4 py-3 rounded-full font-title-sm text-title-sm transition-all
|
||||
{{ $isActive ? 'bg-secondary-container text-on-secondary-container font-semibold' : 'text-on-surface-variant hover:bg-surface-variant/50' }}">
|
||||
<x-icon :name="$it['icon'] ?? ''" class="w-5 h-5 shrink-0" />
|
||||
<span>{{ $it['label'] }}@if (! empty($it['append_department']) && session('department')) - {{ session('department') }}@endif</span>
|
||||
</a>
|
||||
@empty
|
||||
<p class="px-3 py-2 font-body-sm text-body-sm text-on-surface-variant">Moduł w przygotowaniu.</p>
|
||||
@endforelse
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
{{-- ===== Treść ===== --}}
|
||||
<main class="flex-1 p-gutter">
|
||||
{{ $slot }}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<footer class="border-t border-outline-variant/30 px-gutter py-3 text-center font-body-sm text-body-sm text-on-surface-variant">
|
||||
© SoftSPM Przemysław Zatorski · v.2.0
|
||||
</footer>
|
||||
|
||||
{{-- ===== Globalny preloader (pokazywany gdy nawigacja/wyszukiwanie trwa dłużej) ===== --}}
|
||||
<div id="page-loader" class="fixed inset-0 z-[70] hidden items-center justify-center bg-on-surface/30 backdrop-blur-sm">
|
||||
<div class="flex flex-col items-center gap-3 bg-surface-container-lowest/95 px-7 py-6 rounded-xl shadow-card border border-outline-variant/40">
|
||||
<div class="w-12 h-12 rounded-full border-4 border-secondary/25 border-t-secondary animate-spin"></div>
|
||||
<span class="font-data-label text-data-label text-on-surface-variant tracking-wide">Ładowanie...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ===== Wspólny modal potwierdzenia (wyśrodkowany) ===== --}}
|
||||
<div id="confirm-modal" class="fixed inset-0 z-50 hidden items-center justify-center p-4">
|
||||
<div class="absolute inset-0 bg-on-surface/40 backdrop-blur-sm" data-confirm-cancel></div>
|
||||
<div class="relative w-full max-w-md bg-surface-container-lowest rounded-xl shadow-card border border-outline-variant/40 p-6 space-y-4">
|
||||
<h3 id="confirm-modal-title" class="font-headline-md text-headline-md text-on-surface">Potwierdzenie</h3>
|
||||
<p id="confirm-modal-message" class="font-body-md text-body-md text-on-surface-variant">Czy na pewno?</p>
|
||||
<div class="flex justify-end gap-2 pt-2">
|
||||
<button type="button" data-confirm-cancel
|
||||
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</button>
|
||||
<button type="button" id="confirm-modal-ok"
|
||||
class="bg-error text-on-error px-5 py-2.5 rounded-full font-title-sm text-title-sm hover:opacity-90 active:scale-95 transition-all">Usuń</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const modal = document.getElementById('confirm-modal');
|
||||
if (!modal) return;
|
||||
const titleEl = document.getElementById('confirm-modal-title');
|
||||
const msgEl = document.getElementById('confirm-modal-message');
|
||||
const okBtn = document.getElementById('confirm-modal-ok');
|
||||
let pendingForm = null;
|
||||
|
||||
function open(form) {
|
||||
pendingForm = form;
|
||||
titleEl.textContent = form.dataset.confirmTitle || 'Potwierdzenie usunięcia';
|
||||
msgEl.textContent = form.dataset.confirmMessage || 'Czy na pewno?';
|
||||
okBtn.textContent = form.dataset.confirmButton || 'Usuń';
|
||||
modal.classList.remove('hidden');
|
||||
modal.classList.add('flex');
|
||||
}
|
||||
function close() {
|
||||
modal.classList.add('hidden');
|
||||
modal.classList.remove('flex');
|
||||
pendingForm = null;
|
||||
}
|
||||
|
||||
document.addEventListener('click', function (e) {
|
||||
const trigger = e.target.closest('[data-confirm-trigger]');
|
||||
if (trigger) { e.preventDefault(); open(trigger.closest('form')); return; }
|
||||
if (e.target.closest('[data-confirm-cancel]')) { close(); }
|
||||
});
|
||||
okBtn.addEventListener('click', function () { if (pendingForm) pendingForm.submit(); });
|
||||
document.addEventListener('keydown', function (e) { if (e.key === 'Escape') close(); });
|
||||
})();
|
||||
</script>
|
||||
|
||||
{{-- Globalny preloader: pojawia się tylko gdy odpowiedź serwera trwa dłużej niż DELAY --}}
|
||||
<script>
|
||||
(function () {
|
||||
const loader = document.getElementById('page-loader');
|
||||
if (!loader) return;
|
||||
const DELAY = 250; // ms - pokaż dopiero gdy ładowanie się przeciąga
|
||||
let timer = null;
|
||||
|
||||
function show() { loader.classList.remove('hidden'); loader.classList.add('flex'); }
|
||||
function hide() { clearTimeout(timer); loader.classList.add('hidden'); loader.classList.remove('flex'); }
|
||||
function schedule() { clearTimeout(timer); timer = setTimeout(show, DELAY); }
|
||||
|
||||
// Wysłanie formularza (wyszukiwarki, filtry, zapisy) - oprócz otwieranych w nowej karcie.
|
||||
document.addEventListener('submit', function (e) {
|
||||
const form = e.target;
|
||||
if (form.matches && form.matches('form') && form.target !== '_blank') schedule();
|
||||
}, true);
|
||||
|
||||
// Nawigacja kliknięciem w wewnętrzny link (paginacja, menu, akcje).
|
||||
document.addEventListener('click', function (e) {
|
||||
const a = e.target.closest && e.target.closest('a[href]');
|
||||
if (!a) return;
|
||||
const href = a.getAttribute('href');
|
||||
if (!href || href.charAt(0) === '#' || a.target === '_blank' || a.hasAttribute('download')) return;
|
||||
if (a.origin && a.origin !== window.location.origin) return; // tylko ten sam serwer
|
||||
if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
||||
schedule();
|
||||
});
|
||||
|
||||
// Powrót z cache przeglądarki (przycisk Wstecz) - ukryj ewentualny preloader.
|
||||
window.addEventListener('pageshow', hide);
|
||||
})();
|
||||
</script>
|
||||
|
||||
{{-- Motyw (jasny/ciemny) + menu użytkownika --}}
|
||||
<script>
|
||||
(function () {
|
||||
// ===== Motyw - zapis w cookie na ~400 dni, odświeżany przy każdej wizycie =====
|
||||
const COOKIE_MAX_AGE = 60 * 60 * 24 * 400; // sekundy (~400 dni, maksimum przeglądarek)
|
||||
function applyTheme(t) {
|
||||
const r = document.documentElement;
|
||||
r.classList.toggle('dark', t === 'dark');
|
||||
r.classList.toggle('light', t !== 'dark');
|
||||
document.cookie = 'theme=' + t + ';path=/;max-age=' + COOKIE_MAX_AGE + ';samesite=lax';
|
||||
}
|
||||
// Odśwież okno ważności cookie (utrzymanie wyboru "na zawsze" mimo limitu przeglądarki).
|
||||
applyTheme(document.documentElement.classList.contains('dark') ? 'dark' : 'light');
|
||||
|
||||
const toggle = document.getElementById('theme-toggle');
|
||||
if (toggle) toggle.addEventListener('click', function () {
|
||||
applyTheme(document.documentElement.classList.contains('dark') ? 'light' : 'dark');
|
||||
});
|
||||
|
||||
// ===== Menu użytkownika (rozwijane) =====
|
||||
const btn = document.getElementById('user-menu-button');
|
||||
const panel = document.getElementById('user-menu-panel');
|
||||
if (btn && panel) {
|
||||
function closeMenu() { panel.classList.add('hidden'); btn.setAttribute('aria-expanded', 'false'); }
|
||||
btn.addEventListener('click', function (e) {
|
||||
e.stopPropagation();
|
||||
const willOpen = panel.classList.contains('hidden');
|
||||
panel.classList.toggle('hidden');
|
||||
btn.setAttribute('aria-expanded', String(willOpen));
|
||||
});
|
||||
document.addEventListener('click', function (e) {
|
||||
if (!panel.contains(e.target) && !btn.contains(e.target)) closeMenu();
|
||||
});
|
||||
document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeMenu(); });
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,24 @@
|
||||
@props([
|
||||
'variant' => 'secondary', // primary | secondary | ghost | danger
|
||||
'as' => 'button', // 'button' | 'a'
|
||||
'disabled' => false,
|
||||
])
|
||||
|
||||
@php
|
||||
// Jednolity styl przycisków dla CAŁEGO projektu. Zawsze pigułka px-5 py-2.5.
|
||||
$base = 'inline-flex items-center justify-center px-5 py-2.5 rounded-full font-title-sm text-title-sm transition-all';
|
||||
$variants = [
|
||||
'primary' => 'bg-primary text-on-primary hover:bg-primary-container active:scale-95 shadow-card',
|
||||
'secondary' => 'bg-secondary text-on-secondary hover:opacity-90 active:scale-95',
|
||||
'ghost' => 'text-on-surface-variant hover:bg-surface-container',
|
||||
'danger' => 'bg-error text-on-error hover:opacity-90 active:scale-95',
|
||||
];
|
||||
$v = $variants[$variant] ?? $variants['secondary'];
|
||||
$cls = $base.' '.$v.($disabled ? ' opacity-50 cursor-not-allowed pointer-events-none' : '');
|
||||
@endphp
|
||||
|
||||
@if ($as === 'a')
|
||||
<a {{ $attributes->merge(['class' => $cls]) }}>{{ $slot }}</a>
|
||||
@else
|
||||
<button @disabled($disabled) {{ $attributes->merge(['type' => 'button', 'class' => $cls]) }}>{{ $slot }}</button>
|
||||
@endif
|
||||
@@ -0,0 +1,20 @@
|
||||
@props([
|
||||
'action',
|
||||
'message' => 'Czy na pewno usunąć ten obiekt? Tej operacji nie można cofnąć.',
|
||||
'title' => 'Potwierdzenie usunięcia',
|
||||
'button' => 'Usuń',
|
||||
'label' => 'Usuń',
|
||||
])
|
||||
|
||||
{{-- Formularz usuwania - przycisk otwiera wspólny modal (patrz app-layout). --}}
|
||||
<form method="POST" action="{{ $action }}"
|
||||
data-confirm-message="{{ $message }}"
|
||||
data-confirm-title="{{ $title }}"
|
||||
data-confirm-button="{{ $button }}">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="button" data-confirm-trigger
|
||||
class="bg-error text-on-error px-4 py-2 rounded-full font-title-sm text-title-sm hover:opacity-90 transition-all active:scale-95">
|
||||
{{ $label }}
|
||||
</button>
|
||||
</form>
|
||||
@@ -0,0 +1,35 @@
|
||||
@props([
|
||||
'points', // kolekcja {x, y}
|
||||
'xLabel' => 'Q',
|
||||
'yLabel' => 'Y',
|
||||
'color' => '#006876',
|
||||
])
|
||||
|
||||
@php
|
||||
$w = 340; $h = 210; $padL = 40; $padR = 12; $padT = 12; $padB = 26;
|
||||
$plotW = $w - $padL - $padR; $plotH = $h - $padT - $padB;
|
||||
$xMax = max(0.0001, (float) $points->max('x'));
|
||||
$yMax = max(0.0001, (float) $points->max('y'));
|
||||
$sx = fn ($x) => $padL + ($x / $xMax) * $plotW;
|
||||
$sy = fn ($y) => $padT + $plotH - ($y / $yMax) * $plotH;
|
||||
$poly = $points->map(fn ($p) => round($sx($p->x), 1).','.round($sy($p->y), 1))->implode(' ');
|
||||
@endphp
|
||||
|
||||
<svg viewBox="0 0 {{ $w }} {{ $h }}" class="w-full h-auto" xmlns="http://www.w3.org/2000/svg">
|
||||
{{-- osie --}}
|
||||
<line x1="{{ $padL }}" y1="{{ $padT }}" x2="{{ $padL }}" y2="{{ $padT + $plotH }}" stroke="#cbd5d6" stroke-width="1"/>
|
||||
<line x1="{{ $padL }}" y1="{{ $padT + $plotH }}" x2="{{ $padL + $plotW }}" y2="{{ $padT + $plotH }}" stroke="#cbd5d6" stroke-width="1"/>
|
||||
{{-- krzywa --}}
|
||||
<polyline points="{{ $poly }}" fill="none" stroke="{{ $color }}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
{{-- punkty --}}
|
||||
@foreach ($points as $p)
|
||||
<circle cx="{{ round($sx($p->x), 1) }}" cy="{{ round($sy($p->y), 1) }}" r="3" fill="{{ $color }}"/>
|
||||
@endforeach
|
||||
{{-- skrajne wartości osi --}}
|
||||
<text x="{{ $padL - 4 }}" y="{{ $padT + 6 }}" font-size="8" fill="#888" text-anchor="end">{{ rtrim(rtrim(number_format($yMax, 1, '.', ''), '0'), '.') }}</text>
|
||||
<text x="{{ $padL - 4 }}" y="{{ $padT + $plotH }}" font-size="8" fill="#888" text-anchor="end">0</text>
|
||||
<text x="{{ $padL + $plotW }}" y="{{ $padT + $plotH + 12 }}" font-size="8" fill="#888" text-anchor="end">{{ rtrim(rtrim(number_format($xMax, 2, '.', ''), '0'), '.') }}</text>
|
||||
{{-- etykiety osi --}}
|
||||
<text x="{{ $padL }}" y="{{ $padT - 3 }}" font-size="9" fill="#555">{{ $yLabel }}</text>
|
||||
<text x="{{ $padL + $plotW }}" y="{{ $h - 4 }}" font-size="9" fill="#555" text-anchor="end">{{ $xLabel }}</text>
|
||||
</svg>
|
||||
@@ -0,0 +1,55 @@
|
||||
@props([
|
||||
'eta', // sprawność η (0..~0.7) lub null
|
||||
'ry', // granica czerwony/żółty (η)
|
||||
'yg', // granica żółty/zielony (η)
|
||||
'zone' => null, // 'green'|'yellow'|'red' (kolor opisu)
|
||||
])
|
||||
|
||||
@php
|
||||
// Skala 0..0.7 jak legacy. Pozycja znormalizowana: 0=lewo (wysoka η, zielona), 1=prawo.
|
||||
$clamp = fn ($v) => max(0.0, min(1.0, $v));
|
||||
$pYG = $clamp(1 - ($yg / 0.7)); // granica zielony/żółty
|
||||
$pRY = $clamp(1 - ($ry / 0.7)); // granica żółty/czerwony
|
||||
$pYG = min($pYG, $pRY);
|
||||
$pEta = $eta === null ? null : $clamp(1 - ($eta / 0.7));
|
||||
|
||||
// Wyraźne strefy z krótkim przejściem (jak oryginał: green | yellow | red).
|
||||
$t = 0.03;
|
||||
$stops = [];
|
||||
$prev = 0.0;
|
||||
$add = function ($off, $color) use (&$stops, &$prev, $clamp) {
|
||||
$off = max($prev, $clamp($off)); // offsety rosnące
|
||||
$stops[] = [round($off * 100, 2), $color];
|
||||
$prev = $off;
|
||||
};
|
||||
$add(0, '#1f9d57');
|
||||
$add($pYG - $t, '#1f9d57');
|
||||
$add($pYG + $t, '#f4c20d');
|
||||
$add($pRY - $t, '#f4c20d');
|
||||
$add($pRY + $t, '#d32f2f');
|
||||
$add(1, '#d32f2f');
|
||||
|
||||
$id = 'eg-'.substr(md5(($eta ?? 0).$ry.$yg), 0, 6);
|
||||
$pct = fn ($v) => round($v * 100, 2);
|
||||
$zoneColor = ['green' => '#1f9d57', 'yellow' => '#f4c20d', 'red' => '#d32f2f'][$zone] ?? '#5b6566';
|
||||
@endphp
|
||||
|
||||
@if ($eta === null)
|
||||
<div class="h-10 rounded bg-surface-container grid place-items-center font-body-sm text-body-sm text-on-surface-variant">
|
||||
Brak oceny
|
||||
</div>
|
||||
@else
|
||||
<svg viewBox="0 0 100 16" class="w-full h-12" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="{{ $id }}" x1="0" y1="0" x2="1" y2="0">
|
||||
@foreach ($stops as [$off, $color])
|
||||
<stop offset="{{ $off }}%" stop-color="{{ $color }}"/>
|
||||
@endforeach
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="0" y="3" width="100" height="10" rx="1.5" fill="url(#{{ $id }})"/>
|
||||
{{-- Znacznik (dzwon) - pozycja wg η --}}
|
||||
<line x1="{{ $pct($pEta) }}" y1="0.5" x2="{{ $pct($pEta) }}" y2="15.5" stroke="#101415" stroke-width="0.9"/>
|
||||
<line x1="{{ $pct($pEta) }}" y1="0.5" x2="{{ $pct($pEta) }}" y2="15.5" stroke="#ffffff" stroke-width="0.3"/>
|
||||
</svg>
|
||||
@endif
|
||||
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="light" lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ $title ?? 'SoftSPM 2' }}</title>
|
||||
|
||||
{{-- Air-gap: brak CDN. Assety budowane lokalnie przez Vite, fonty self-hosted. --}}
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
</head>
|
||||
<body class="min-h-screen bg-surface text-on-surface antialiased">
|
||||
{{ $slot }}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,31 @@
|
||||
@props(['name' => '', 'class' => 'w-5 h-5'])
|
||||
|
||||
@php
|
||||
// Ikony jako wbudowane SVG (Lucide-style) - działają offline, bez fontu.
|
||||
// Klucze = nazwy 'icon' z config/navigation.php.
|
||||
$paths = [
|
||||
'home' => '<path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/>',
|
||||
'map' => '<path d="M3 6 9 3l6 3 6-3v15l-6 3-6-3-6 3z"/><path d="M9 3v15"/><path d="M15 6v15"/>',
|
||||
'inventory_2' => '<path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/><path d="m3.3 7 8.7 5 8.7-5"/><path d="M12 22V12"/>',
|
||||
'monitoring' => '<path d="M3 12h4l3 8 4-16 3 8h4"/>',
|
||||
'engineering' => '<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>',
|
||||
'biotech' => '<path d="M10 2v6.3a1 1 0 0 1-.4.8l-3.6 2.6A4 4 0 0 0 8.5 19h7a4 4 0 0 0 2.5-7.3l-3.6-2.6a1 1 0 0 1-.4-.8V2"/><path d="M8.5 2h7"/><path d="M7 16h10"/>',
|
||||
'build' => '<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>',
|
||||
'settings' => '<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>',
|
||||
'menu_book' => '<path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/>',
|
||||
'water_drop' => '<path d="M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5C4 11.1 3 13 3 15a7 7 0 0 0 7 7z"/>',
|
||||
'bolt' => '<path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/>',
|
||||
'precision_manufacturing' => '<path d="M2 20a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8l-7 5V8l-7 5V4a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"/><path d="M7 18h.01"/><path d="M12 18h.01"/><path d="M17 18h.01"/>',
|
||||
'checklist' => '<path d="m3 17 2 2 4-4"/><path d="m3 7 2 2 4-4"/><path d="M13 6h8"/><path d="M13 12h8"/><path d="M13 18h8"/>',
|
||||
'cable' => '<path d="M5 3v4"/><path d="M3 5h4"/><path d="M19 17v4"/><path d="M17 19h4"/><path d="M5 7a7 7 0 0 0 7 7 7 7 0 0 1 7 7"/>',
|
||||
'plumbing' => '<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>',
|
||||
'speed' => '<path d="m12 14 4-4"/><path d="M3.34 19a10 10 0 1 1 17.32 0"/>',
|
||||
];
|
||||
$svg = $paths[$name] ?? '<circle cx="12" cy="12" r="2"/>';
|
||||
@endphp
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
||||
{{ $attributes->merge(['class' => $class]) }}>
|
||||
{!! $svg !!}
|
||||
</svg>
|
||||
@@ -0,0 +1,96 @@
|
||||
@props([
|
||||
'power' => null, // ['pe','pod','paz','psl','psa','pp2','pst','pgh','sum'] lub null
|
||||
'on' => false,
|
||||
'time' => null, // czas pomiaru
|
||||
])
|
||||
|
||||
@php
|
||||
// Schemat układu pompowego (zastępuje statyczny rozklad_mocy.png). Bez strzałek -
|
||||
// rozkład mocy opisany legendą pod rysunkiem (każda składowa = inny kolor).
|
||||
$f = fn ($v) => $v === null ? '-' : number_format(round((float) $v, 2), 2, ',', ' ');
|
||||
$pumpColor = $on ? '#16a34a' : '#9aa6a8';
|
||||
$lab = '#2f3e45';
|
||||
|
||||
// Legenda: [kolor (unikalny), opis, klucz wartości].
|
||||
$legend = [
|
||||
['#ff7a1a', 'Moc pobrana - kabel przyłączeniowy', 'pe'],
|
||||
['#d32f2f', 'Moc na wyjściu pompy (agregatu)', 'pod'],
|
||||
['#2563eb', 'Podnoszenie stałe (do zwierciadła statycznego)', 'pst'],
|
||||
['#0891b2', 'Geo-hydraulika (na filtrach studni)', 'pgh'],
|
||||
['#7c3aed', 'Odpływ (za zasuwą dławiącą)', 'pp2'],
|
||||
['#f59e0b', 'Straty: agregat + zasilanie', 'paz'],
|
||||
['#65a30d', 'Straty liniowe (rurociąg tłoczny)', 'psl'],
|
||||
['#db2777', 'Straty w armaturze (zawór)', 'psa'],
|
||||
];
|
||||
@endphp
|
||||
|
||||
@if ($power === null)
|
||||
<div class="py-10 text-center font-body-sm text-body-sm text-on-surface-variant">Brak danych pomiarowych do bilansu mocy.</div>
|
||||
@else
|
||||
<svg viewBox="110 0 350 580" class="w-full h-auto max-h-[30rem] mx-auto block" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<pattern id="ps-ground" width="8" height="8" patternTransform="rotate(45)" patternUnits="userSpaceOnUse">
|
||||
<line x1="0" y1="0" x2="0" y2="8" stroke="#b9a58c" stroke-width="2"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
{{-- ===== Instalacja powierzchniowa ===== --}}
|
||||
<rect x="216" y="90" width="8" height="56" fill="#7aa7c7"/>
|
||||
<path d="M220 92 H360" stroke="#7aa7c7" stroke-width="8" fill="none"/>
|
||||
{{-- zawór dławiący --}}
|
||||
<polygon points="248,82 248,102 272,82 272,102" fill="#e0e6ea" stroke="#3b3b3b" stroke-width="1.2"/>
|
||||
<text x="244" y="80" font-size="8" fill="{{ $lab }}" text-anchor="end">zawór</text>
|
||||
{{-- manometry + Q --}}
|
||||
<circle cx="234" cy="68" r="7" fill="#fff" stroke="#3b3b3b" stroke-width="1.3"/><text x="234" y="57" font-size="9" fill="{{ $lab }}" text-anchor="middle">p₁</text>
|
||||
<circle cx="296" cy="68" r="7" fill="#fff" stroke="#3b3b3b" stroke-width="1.3"/><text x="296" y="57" font-size="9" fill="{{ $lab }}" text-anchor="middle">p₂</text>
|
||||
<rect x="316" y="80" width="16" height="14" rx="2" fill="#fff" stroke="#3b3b3b" stroke-width="1.3"/><text x="324" y="91" font-size="8" font-weight="700" fill="#3b3b3b" text-anchor="middle">Q</text>
|
||||
{{-- głowica/silnik nawierzchniowy + skrzynka kabla --}}
|
||||
<rect x="198" y="120" width="56" height="22" rx="2" fill="#cfd8dd" stroke="#5b6566"/>
|
||||
<rect x="198" y="104" width="18" height="16" rx="2" fill="#37424a"/>
|
||||
<text x="194" y="100" font-size="8" fill="{{ $lab }}" text-anchor="end">kabel</text>
|
||||
|
||||
{{-- ===== Grunt + studnia ===== --}}
|
||||
<rect x="0" y="175" width="460" height="14" fill="url(#ps-ground)"/>
|
||||
<line x1="0" y1="175" x2="460" y2="175" stroke="#8f706c" stroke-width="1.5"/>
|
||||
<line x1="196" y1="175" x2="196" y2="540" stroke="#5b6566" stroke-width="3"/>
|
||||
<line x1="246" y1="175" x2="246" y2="540" stroke="#5b6566" stroke-width="3"/>
|
||||
<line x1="196" y1="540" x2="246" y2="540" stroke="#5b6566" stroke-width="3"/>
|
||||
<rect x="198" y="345" width="48" height="195" fill="#cfeaf5" opacity="0.55"/>
|
||||
|
||||
{{-- zwierciadła --}}
|
||||
<line x1="196" y1="315" x2="246" y2="315" stroke="#1565c0" stroke-width="1.3" stroke-dasharray="4 3"/>
|
||||
<text x="252" y="318" font-size="9" fill="{{ $lab }}">H<tspan baseline-shift="sub" font-size="6.5">st</tspan></text>
|
||||
<line x1="196" y1="345" x2="246" y2="345" stroke="#1565c0" stroke-width="1.8"/>
|
||||
<text x="252" y="348" font-size="9" fill="{{ $lab }}">H<tspan baseline-shift="sub" font-size="6.5">d</tspan></text>
|
||||
|
||||
{{-- rurociąg pionowy + kabel zasilający --}}
|
||||
<rect x="216" y="142" width="8" height="332" fill="#7aa7c7"/>
|
||||
<line x1="208" y1="142" x2="208" y2="470" stroke="#37424a" stroke-width="2.5"/>
|
||||
|
||||
{{-- filtry studni (perforacja) --}}
|
||||
@for ($i = 0; $i < 7; $i++)
|
||||
<line x1="188" y1="{{ 474 + $i * 9 }}" x2="196" y2="{{ 474 + $i * 9 }}" stroke="#5b6566" stroke-width="2"/>
|
||||
<line x1="246" y1="{{ 474 + $i * 9 }}" x2="254" y2="{{ 474 + $i * 9 }}" stroke="#5b6566" stroke-width="2"/>
|
||||
@endfor
|
||||
<text x="184" y="524" font-size="8" fill="{{ $lab }}" text-anchor="end">filtry</text>
|
||||
|
||||
{{-- agregat: pompa + silnik --}}
|
||||
<rect x="206" y="472" width="30" height="36" rx="3" fill="{{ $pumpColor }}" stroke="#2f3e45" stroke-width="1.5"/>
|
||||
<rect x="211" y="508" width="20" height="18" rx="2" fill="#46555d"/>
|
||||
<text x="262" y="494" font-size="8" fill="{{ $lab }}">agregat</text>
|
||||
|
||||
{{-- Czas pomiaru --}}
|
||||
<text x="452" y="566" font-size="11" fill="#1d4ed8" text-anchor="end">Czas pomiaru: {{ $time ?? '-' }}</text>
|
||||
</svg>
|
||||
|
||||
{{-- Legenda: kolor + opis + wartość (każda składowa innym kolorem) --}}
|
||||
<div class="mt-3 grid grid-cols-1 sm:grid-cols-2 gap-x-5 gap-y-1.5 font-body-sm text-body-sm">
|
||||
@foreach ($legend as [$color, $desc, $key])
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="inline-block w-3.5 h-3.5 rounded-sm shrink-0" style="background: {{ $color }}"></span>
|
||||
<span class="text-on-surface-variant flex-1">{{ $desc }}</span>
|
||||
<span class="font-data-label text-on-surface whitespace-nowrap">{{ $f($power[$key]) }} kW</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
@@ -0,0 +1,178 @@
|
||||
@props([
|
||||
'plot', // ['segments','raw','xMax','yMax','coeffs']
|
||||
'xLabel' => 'Q [m³/h]',
|
||||
'yLabel' => 'Y',
|
||||
'interactive' => false, // przeciągalny marker [Q,H]
|
||||
'id' => 'curve',
|
||||
'initialQ' => null, // początkowe Q markera
|
||||
'etaCoeffs' => [], // współczynniki η (do aktualizacji panelu)
|
||||
'qInput' => null, // selektor pola Q (input)
|
||||
'hOutput' => null, // selektor wyświetlania H
|
||||
'etaOutput' => null, // selektor wyświetlania η
|
||||
'opX' => null, // stały punkt pracy: Q (np. z pomiaru SYNDIS)
|
||||
'opY' => null, // stały punkt pracy: wartość Y w tym Q
|
||||
'opColor' => '#c62828', // kolor punktu pracy
|
||||
'opYLabel' => null, // gdy ustawione - przy Q pokazuje też tę wartość (np. H)
|
||||
'band' => false, // pionowy pasek podświetlenia przy opX (wykres η)
|
||||
])
|
||||
|
||||
@php
|
||||
$w = 380; $h = 230; $padL = 48; $padR = 12; $padT = 14; $padB = 30;
|
||||
$plotW = $w - $padL - $padR; $plotH = $h - $padT - $padB;
|
||||
$xMax = max(0.0001, (float) $plot['xMax']);
|
||||
$yMax = max(0.0001, (float) $plot['yMax']);
|
||||
$sx = fn ($x) => $padL + ($x / $xMax) * $plotW;
|
||||
$sy = fn ($y) => $padT + $plotH - ($y / $yMax) * $plotH;
|
||||
$fmt = fn ($v) => $v >= 100 ? number_format($v, 0, ',', ' ')
|
||||
: ($v >= 10 ? number_format($v, 1, ',', '') : number_format($v, 2, ',', ''));
|
||||
@endphp
|
||||
|
||||
<svg viewBox="0 0 {{ $w }} {{ $h }}" class="w-full h-auto select-none" xmlns="http://www.w3.org/2000/svg"
|
||||
@if ($interactive)
|
||||
id="{{ $id }}"
|
||||
data-padl="{{ $padL }}" data-padt="{{ $padT }}" data-plotw="{{ $plotW }}" data-ploth="{{ $plotH }}"
|
||||
data-xmax="{{ $xMax }}" data-ymax="{{ $yMax }}"
|
||||
data-coeffs="{{ json_encode(array_values($plot['coeffs'] ?? [])) }}"
|
||||
data-etacoeffs="{{ json_encode(array_values($etaCoeffs ?? [])) }}"
|
||||
data-scope="{{ json_encode($plot['raw']->pluck('x')->values()->all()) }}"
|
||||
@endif
|
||||
>
|
||||
{{-- Siatka + etykiety osi --}}
|
||||
@for ($g = 0; $g <= 4; $g++)
|
||||
@php $gy = $padT + $plotH * $g / 4; $vy = $yMax * (1 - $g / 4); @endphp
|
||||
<line x1="{{ $padL }}" y1="{{ round($gy, 1) }}" x2="{{ $padL + $plotW }}" y2="{{ round($gy, 1) }}" stroke="var(--chart-grid)" stroke-width="1"/>
|
||||
<text x="{{ $padL - 5 }}" y="{{ round($gy + 3, 1) }}" font-size="8" fill="var(--chart-tick)" text-anchor="end">{{ $fmt($vy) }}</text>
|
||||
@endfor
|
||||
@for ($g = 0; $g <= 4; $g++)
|
||||
@php $gx = $padL + $plotW * $g / 4; $vx = $xMax * $g / 4; @endphp
|
||||
<line x1="{{ round($gx, 1) }}" y1="{{ $padT }}" x2="{{ round($gx, 1) }}" y2="{{ $padT + $plotH }}" stroke="var(--chart-grid)" stroke-width="1"/>
|
||||
<text x="{{ round($gx, 1) }}" y="{{ $padT + $plotH + 12 }}" font-size="8" fill="var(--chart-tick)" text-anchor="middle">{{ $fmt($vx) }}</text>
|
||||
@endfor
|
||||
|
||||
{{-- Osie --}}
|
||||
<line x1="{{ $padL }}" y1="{{ $padT }}" x2="{{ $padL }}" y2="{{ $padT + $plotH }}" stroke="var(--chart-axis)" stroke-width="1"/>
|
||||
<line x1="{{ $padL }}" y1="{{ $padT + $plotH }}" x2="{{ $padL + $plotW }}" y2="{{ $padT + $plotH }}" stroke="var(--chart-axis)" stroke-width="1"/>
|
||||
|
||||
{{-- Krzywa wielomianowa - kolorowe segmenty wg stref Q --}}
|
||||
@foreach ($plot['segments'] as $seg)
|
||||
@php $poly = collect($seg['points'])->map(fn ($p) => round($sx($p['x']), 1).','.round($sy($p['y']), 1))->implode(' '); @endphp
|
||||
<polyline points="{{ $poly }}" fill="none" stroke="{{ $seg['color'] }}" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
@endforeach
|
||||
|
||||
{{-- Pasek podświetlenia przy zadanym Q (wykres η) --}}
|
||||
@if ($band && $opX !== null && $opX >= 0 && $opX <= $xMax)
|
||||
<rect x="{{ round($sx($opX) - 6, 1) }}" y="{{ $padT }}" width="12" height="{{ $plotH }}" fill="#f4c20d" opacity="0.22"/>
|
||||
@endif
|
||||
|
||||
{{-- Punkty surowe (niebieskie) --}}
|
||||
@foreach ($plot['raw'] as $p)
|
||||
<circle cx="{{ round($sx($p->x), 1) }}" cy="{{ round($sy($p->y), 1) }}" r="3" fill="var(--chart-raw)"/>
|
||||
@endforeach
|
||||
|
||||
{{-- Stały punkt pracy (wartości zmierzone) - na rzeczywistej wysokości, nie rzutowany na krzywą --}}
|
||||
@if ($opX !== null && $opY !== null && $opX >= 0 && $opX <= $xMax)
|
||||
<circle cx="{{ round($sx($opX), 1) }}" cy="{{ round($sy(max(0, $opY)), 1) }}" r="5" fill="{{ $opColor }}" stroke="#ffffff" stroke-width="1.5"/>
|
||||
<text x="{{ $padL + $plotW }}" y="{{ $padT + 2 }}" font-size="7" fill="var(--color-secondary)" text-anchor="end">Q = {{ number_format(round((float) $opX, 3), 3, ',', '') }}</text>
|
||||
@if ($opYLabel !== null)
|
||||
<text x="{{ $padL + $plotW }}" y="{{ $padT + 11 }}" font-size="7" fill="var(--color-secondary)" text-anchor="end">{{ $opYLabel }} = {{ number_format(round((float) $opY, 1), 1, ',', ' ') }}</text>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
{{-- Interaktywny marker + tooltip --}}
|
||||
@if ($interactive)
|
||||
<g id="{{ $id }}-tip" style="display:none">
|
||||
<rect id="{{ $id }}-tiprect" rx="3" height="18" fill="#181c1d" opacity="0.9"></rect>
|
||||
<text id="{{ $id }}-tiptext" font-size="9" fill="#ffffff" text-anchor="middle"></text>
|
||||
</g>
|
||||
<circle id="{{ $id }}-marker" r="6" fill="#a61317" stroke="#ffffff" stroke-width="2" style="cursor:grab"></circle>
|
||||
@endif
|
||||
|
||||
{{-- Etykiety osi --}}
|
||||
<text x="{{ $padL - 4 }}" y="{{ $padT - 4 }}" font-size="9" fill="var(--chart-label)">{{ $yLabel }}</text>
|
||||
<text x="{{ $padL + $plotW }}" y="{{ $h - 3 }}" font-size="9" fill="var(--chart-label)" text-anchor="end">{{ $xLabel }}</text>
|
||||
</svg>
|
||||
|
||||
@if ($interactive)
|
||||
<p class="font-body-sm text-body-sm text-on-surface-variant mt-1 text-center">
|
||||
Przeciągnij <span class="inline-block w-2 h-2 rounded-full align-middle" style="background:#a61317"></span> po wykresie, aby odczytać H dla danego Q.
|
||||
</p>
|
||||
<script>
|
||||
(function () {
|
||||
function init() {
|
||||
const svg = document.getElementById(@json($id));
|
||||
if (!svg) return;
|
||||
const d = svg.dataset;
|
||||
const padL = +d.padl, padT = +d.padt, plotW = +d.plotw, plotH = +d.ploth, xMax = +d.xmax, yMax = +d.ymax;
|
||||
const hC = JSON.parse(d.coeffs || '[]');
|
||||
const etaC = JSON.parse(d.etacoeffs || '[]');
|
||||
const poly = (c, x) => c.reduce((s, ci, i) => s + ci * Math.pow(x, i), 0);
|
||||
const sx = q => padL + (q / xMax) * plotW;
|
||||
const sy = v => padT + plotH - (Math.max(0, v) / yMax) * plotH;
|
||||
const marker = document.getElementById(@json($id . '-marker'));
|
||||
const tip = document.getElementById(@json($id . '-tip'));
|
||||
const tipR = document.getElementById(@json($id . '-tiprect'));
|
||||
const tipT = document.getElementById(@json($id . '-tiptext'));
|
||||
const qInput = @json($qInput) ? document.querySelector(@json($qInput)) : null;
|
||||
const hOut = @json($hOutput) ? document.querySelector(@json($hOutput)) : null;
|
||||
const etaOut = @json($etaOutput) ? document.querySelector(@json($etaOutput)) : null;
|
||||
const fmt = (v, dd) => v.toFixed(dd).replace('.', ',');
|
||||
|
||||
// Kolor punktu wg przedziału (strefy Q) - jak segmenty krzywej.
|
||||
const scope = JSON.parse(d.scope || '[]');
|
||||
const BAND = { red: '#c62828', amber: '#f9a825', green: '#2e7d32' };
|
||||
function bandColor(q) {
|
||||
if (scope.length < 7) return BAND.green;
|
||||
if (q < scope[2]) return BAND.red;
|
||||
if (q < scope[3]) return BAND.amber;
|
||||
if (q < scope[5]) return BAND.green;
|
||||
if (q < scope[6]) return BAND.amber;
|
||||
return BAND.red;
|
||||
}
|
||||
|
||||
let hovering = false, dragging = false;
|
||||
function tipVisible() { tip.style.display = (hovering || dragging) ? '' : 'none'; }
|
||||
|
||||
function update(q) {
|
||||
q = Math.max(0, Math.min(q, xMax));
|
||||
const hVal = Math.max(0, poly(hC, q));
|
||||
const mx = sx(q), my = sy(hVal);
|
||||
marker.setAttribute('cx', mx);
|
||||
marker.setAttribute('cy', my);
|
||||
marker.setAttribute('fill', bandColor(q));
|
||||
const label = 'Q=' + fmt(q, 3) + ' H=' + fmt(hVal, 2);
|
||||
tipT.textContent = label;
|
||||
const tw = label.length * 5.2 + 10;
|
||||
let tx = Math.max(padL, Math.min(mx - tw / 2, padL + plotW - tw));
|
||||
const ty = Math.max(padT, my - 26);
|
||||
tipR.setAttribute('x', tx); tipR.setAttribute('y', ty); tipR.setAttribute('width', tw);
|
||||
tipT.setAttribute('x', tx + tw / 2); tipT.setAttribute('y', ty + 13);
|
||||
if (qInput) qInput.value = fmt(q, 3);
|
||||
if (hOut) hOut.textContent = fmt(hVal, 2);
|
||||
if (etaOut && etaC.length) etaOut.textContent = fmt(Math.max(0, poly(etaC, q)), 2);
|
||||
}
|
||||
function clientToQ(e) {
|
||||
const pt = svg.createSVGPoint();
|
||||
pt.x = e.clientX; pt.y = e.clientY;
|
||||
const loc = pt.matrixTransform(svg.getScreenCTM().inverse());
|
||||
return (loc.x - padL) / plotW * xMax;
|
||||
}
|
||||
|
||||
svg.style.cursor = 'ew-resize';
|
||||
// Tooltip tylko przy najechaniu na punkt (lub w trakcie przeciągania).
|
||||
marker.addEventListener('pointerenter', () => { hovering = true; tipVisible(); });
|
||||
marker.addEventListener('pointerleave', () => { hovering = false; tipVisible(); });
|
||||
svg.addEventListener('pointerdown', e => { dragging = true; tipVisible(); try { svg.setPointerCapture(e.pointerId); } catch (_) {} update(clientToQ(e)); });
|
||||
svg.addEventListener('pointermove', e => { if (dragging) update(clientToQ(e)); });
|
||||
svg.addEventListener('pointerup', () => { dragging = false; tipVisible(); });
|
||||
svg.addEventListener('pointercancel', () => { dragging = false; tipVisible(); });
|
||||
|
||||
// Domyślnie marker w Q_n; tooltip ukryty do najechania.
|
||||
const initQ = @json($initialQ);
|
||||
update(initQ === null ? xMax : initQ);
|
||||
tipVisible();
|
||||
}
|
||||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
||||
else init();
|
||||
})();
|
||||
</script>
|
||||
@endif
|
||||
@@ -0,0 +1,59 @@
|
||||
@props([
|
||||
'chart', // ['curves'=>[['name','plot'=>[...]],...], 'xMax', 'yMax']
|
||||
'q' => null, // zadane Q - pionowa linia robocza
|
||||
'xLabel' => 'Q [m³/h]',
|
||||
'yLabel' => 'H [m]',
|
||||
])
|
||||
|
||||
@php
|
||||
$w = 760; $h = 360; $padL = 52; $padR = 16; $padT = 16; $padB = 34;
|
||||
$plotW = $w - $padL - $padR; $plotH = $h - $padT - $padB;
|
||||
$xMax = max(0.0001, (float) $chart['xMax']);
|
||||
$yMax = max(0.0001, (float) $chart['yMax']);
|
||||
$sx = fn ($x) => $padL + ($x / $xMax) * $plotW;
|
||||
$sy = fn ($y) => $padT + $plotH - ($y / $yMax) * $plotH;
|
||||
$fmt = fn ($v) => $v >= 100 ? number_format($v, 0, ',', ' ')
|
||||
: ($v >= 10 ? number_format($v, 1, ',', '') : number_format($v, 2, ',', ''));
|
||||
@endphp
|
||||
|
||||
<svg viewBox="0 0 {{ $w }} {{ $h }}" class="w-full h-auto select-none" xmlns="http://www.w3.org/2000/svg">
|
||||
{{-- Siatka + etykiety osi --}}
|
||||
@for ($g = 0; $g <= 4; $g++)
|
||||
@php $gy = $padT + $plotH * $g / 4; $vy = $yMax * (1 - $g / 4); @endphp
|
||||
<line x1="{{ $padL }}" y1="{{ round($gy, 1) }}" x2="{{ $padL + $plotW }}" y2="{{ round($gy, 1) }}" stroke="var(--chart-grid)" stroke-width="1"/>
|
||||
<text x="{{ $padL - 6 }}" y="{{ round($gy + 3, 1) }}" font-size="9" fill="var(--chart-tick)" text-anchor="end">{{ $fmt($vy) }}</text>
|
||||
@endfor
|
||||
@for ($g = 0; $g <= 5; $g++)
|
||||
@php $gx = $padL + $plotW * $g / 5; $vx = $xMax * $g / 5; @endphp
|
||||
<line x1="{{ round($gx, 1) }}" y1="{{ $padT }}" x2="{{ round($gx, 1) }}" y2="{{ $padT + $plotH }}" stroke="var(--chart-grid)" stroke-width="1"/>
|
||||
<text x="{{ round($gx, 1) }}" y="{{ $padT + $plotH + 14 }}" font-size="9" fill="var(--chart-tick)" text-anchor="middle">{{ $fmt($vx) }}</text>
|
||||
@endfor
|
||||
|
||||
{{-- Osie --}}
|
||||
<line x1="{{ $padL }}" y1="{{ $padT }}" x2="{{ $padL }}" y2="{{ $padT + $plotH }}" stroke="var(--chart-axis)" stroke-width="1"/>
|
||||
<line x1="{{ $padL }}" y1="{{ $padT + $plotH }}" x2="{{ $padL + $plotW }}" y2="{{ $padT + $plotH }}" stroke="var(--chart-axis)" stroke-width="1"/>
|
||||
|
||||
{{-- Linia robocza przy zadanym Q --}}
|
||||
@if ($q !== null && $q > 0 && $q <= $xMax)
|
||||
<line x1="{{ round($sx($q), 1) }}" y1="{{ $padT }}" x2="{{ round($sx($q), 1) }}" y2="{{ $padT + $plotH }}"
|
||||
stroke="var(--color-secondary)" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="{{ round($sx($q), 1) }}" y="{{ $padT - 4 }}" font-size="9" fill="var(--color-secondary)" text-anchor="middle">Q = {{ $fmt($q) }}</text>
|
||||
@endif
|
||||
|
||||
{{-- Nałożone krzywe H wszystkich dopasowanych pomp (kolorowe segmenty stref Q) --}}
|
||||
@foreach ($chart['curves'] as $curve)
|
||||
@foreach ($curve['plot']['segments'] as $seg)
|
||||
@php $poly = collect($seg['points'])->map(fn ($p) => round($sx($p['x']), 1).','.round($sy($p['y']), 1))->implode(' '); @endphp
|
||||
<polyline points="{{ $poly }}" fill="none" stroke="{{ $seg['color'] }}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" opacity="0.85"/>
|
||||
@endforeach
|
||||
{{-- Nazwa pompy przy końcu krzywej --}}
|
||||
@php $last = collect($curve['plot']['raw'])->last(); @endphp
|
||||
@if ($last)
|
||||
<text x="{{ round($sx($last->x) + 3, 1) }}" y="{{ round($sy($last->y), 1) }}" font-size="8" fill="var(--chart-label)">{{ $curve['name'] }}</text>
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
{{-- Etykiety osi --}}
|
||||
<text x="{{ $padL - 6 }}" y="{{ $padT - 5 }}" font-size="10" fill="var(--chart-label)">{{ $yLabel }}</text>
|
||||
<text x="{{ $padL + $plotW }}" y="{{ $h - 4 }}" font-size="10" fill="var(--chart-label)" text-anchor="end">{{ $xLabel }}</text>
|
||||
</svg>
|
||||
@@ -0,0 +1,32 @@
|
||||
@props(['column', 'align' => 'left'])
|
||||
|
||||
{{--
|
||||
Klikalny nagłówek tabeli z sortowaniem.
|
||||
- 'column' : klucz sortowania (musi być na whiteliście w kontrolerze).
|
||||
- 'align' : left | right | center (domyślnie left).
|
||||
Zachowuje bieżące filtry (cały query string), resetuje paginację (?page).
|
||||
Strzałka: aktywna kolumna = kierunek (rosnąco/malejąco), pozostałe = neutralna.
|
||||
--}}
|
||||
|
||||
@php
|
||||
$activeSort = request('sort');
|
||||
$activeDir = request('dir', 'asc') === 'desc' ? 'desc' : 'asc';
|
||||
$isActive = $activeSort === $column;
|
||||
$nextDir = $isActive && $activeDir === 'asc' ? 'desc' : 'asc';
|
||||
|
||||
$query = array_merge(request()->except('page'), ['sort' => $column, 'dir' => $nextDir]);
|
||||
$url = request()->url() . '?' . http_build_query($query);
|
||||
|
||||
$alignClass = ['left' => 'text-left', 'right' => 'text-right', 'center' => 'text-center'][$align] ?? 'text-left';
|
||||
$justifyClass = ['left' => 'justify-start', 'right' => 'justify-end', 'center' => 'justify-center'][$align] ?? 'justify-start';
|
||||
|
||||
$arrow = $isActive ? ($activeDir === 'asc' ? '▲' : '▼') : '↕';
|
||||
@endphp
|
||||
|
||||
<th {{ $attributes->merge(['class' => "px-4 py-3 $alignClass"]) }}>
|
||||
<a href="{{ $url }}"
|
||||
class="group inline-flex items-center gap-1 {{ $justifyClass }} hover:text-primary transition-colors select-none">
|
||||
<span>{{ $slot }}</span>
|
||||
<span class="text-[0.85em] leading-none {{ $isActive ? 'text-primary' : 'text-on-surface-variant/40 group-hover:text-on-surface-variant' }}">{{ $arrow }}</span>
|
||||
</a>
|
||||
</th>
|
||||
@@ -0,0 +1,19 @@
|
||||
@props([
|
||||
'name',
|
||||
'label',
|
||||
'value' => null,
|
||||
'type' => 'text',
|
||||
'required' => false,
|
||||
'readonly' => false,
|
||||
])
|
||||
|
||||
<div {{ $attributes->only('class')->merge(['class' => 'space-y-1']) }}>
|
||||
<label for="{{ $name }}" class="font-data-label text-data-label text-on-surface-variant tracking-wide">
|
||||
{{ $label }}@if ($required) <span class="text-primary">*</span>@endif
|
||||
</label>
|
||||
<input id="{{ $name }}" name="{{ $name }}" type="{{ $type }}"
|
||||
value="{{ old($name, $value) }}"
|
||||
@required($required) @readonly($readonly)
|
||||
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-data-label text-on-surface transition-all
|
||||
{{ $readonly ? 'opacity-70 cursor-not-allowed' : '' }}">
|
||||
</div>
|
||||
@@ -0,0 +1,92 @@
|
||||
@props([
|
||||
'throttle' => null, // strefa oceny dławienia: green|yellow|red|null
|
||||
'loss' => null, // strefa oceny strat liniowych
|
||||
'on' => false, // czy pompa załączona
|
||||
'grey' => false, // tryb bez oceny (SPM - inne)
|
||||
'type' => 'SPM', // etykieta typu (BASE/OPT/FULL)
|
||||
])
|
||||
|
||||
@php
|
||||
// Dynamiczny schemat układu pompowego (zastępuje statyczne PNG SPM_*).
|
||||
// Kolory podstawiane wg ocen: zawór dławiący = dławienie, rurociąg = straty.
|
||||
$zc = ['green' => '#2e7d32', 'yellow' => '#f9a825', 'red' => '#c62828'];
|
||||
$valveColor = $grey ? '#9aa6a8' : ($zc[$throttle] ?? '#9aa6a8');
|
||||
$pipeColor = $grey ? '#9aa6a8' : ($zc[$loss] ?? '#9aa6a8');
|
||||
$pumpColor = $on ? '#16a34a' : '#9aa6a8';
|
||||
$label = '#2f3e45'; // stały ciemny - schemat na jasnym podkładzie w obu motywach
|
||||
@endphp
|
||||
|
||||
<svg viewBox="0 0 360 480" class="w-full h-auto max-h-[28rem] mx-auto" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<pattern id="ws-ground" width="8" height="8" patternTransform="rotate(45)" patternUnits="userSpaceOnUse">
|
||||
<line x1="0" y1="0" x2="0" y2="8" stroke="#b9a58c" stroke-width="2"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
{{-- ===== Instalacja powierzchniowa (tłoczenie) ===== --}}
|
||||
{{-- pion z głowicy do poziomu --}}
|
||||
<rect x="156" y="60" width="8" height="62" fill="{{ $pipeColor }}"/>
|
||||
{{-- kolano + rurociąg tłoczny poziomy --}}
|
||||
<path d="M156 64 H300" stroke="{{ $pipeColor }}" stroke-width="8" fill="none"/>
|
||||
|
||||
{{-- zawór dławiący (kolor = ocena dławienia) --}}
|
||||
<g>
|
||||
<polygon points="214,52 214,76 238,52 238,76" fill="{{ $valveColor }}" stroke="#3b3b3b" stroke-width="1.2"/>
|
||||
<line x1="226" y1="48" x2="226" y2="40" stroke="#3b3b3b" stroke-width="2"/>
|
||||
<rect x="219" y="36" width="14" height="5" rx="1" fill="#3b3b3b"/>
|
||||
</g>
|
||||
|
||||
{{-- manometry p1 / p2 --}}
|
||||
<circle cx="196" cy="46" r="9" fill="#ffffff" stroke="#3b3b3b" stroke-width="1.5"/>
|
||||
<line x1="196" y1="55" x2="196" y2="61" stroke="#3b3b3b" stroke-width="2"/>
|
||||
<text x="196" y="32" font-size="11" font-weight="600" fill="{{ $label }}" text-anchor="middle">p₁</text>
|
||||
|
||||
<circle cx="258" cy="46" r="9" fill="#ffffff" stroke="#3b3b3b" stroke-width="1.5"/>
|
||||
<line x1="258" y1="55" x2="258" y2="61" stroke="#3b3b3b" stroke-width="2"/>
|
||||
<text x="258" y="32" font-size="11" font-weight="600" fill="{{ $label }}" text-anchor="middle">p₂</text>
|
||||
|
||||
{{-- przepływomierz Q --}}
|
||||
<rect x="280" y="55" width="20" height="18" rx="2" fill="#ffffff" stroke="#3b3b3b" stroke-width="1.5"/>
|
||||
<text x="290" y="68" font-size="10" font-weight="700" fill="#3b3b3b" text-anchor="middle">Q</text>
|
||||
{{-- wylot --}}
|
||||
<path d="M300 64 H324 M318 58 L324 64 L318 70" stroke="#3b3b3b" stroke-width="2.5" fill="none"/>
|
||||
|
||||
{{-- ===== Grunt ===== --}}
|
||||
<rect x="0" y="122" width="360" height="14" fill="url(#ws-ground)"/>
|
||||
<line x1="0" y1="122" x2="360" y2="122" stroke="#8f706c" stroke-width="1.5"/>
|
||||
|
||||
{{-- ===== Otwór studni (obudowa) ===== --}}
|
||||
<line x1="138" y1="122" x2="138" y2="452" stroke="#5b6566" stroke-width="3"/>
|
||||
<line x1="182" y1="122" x2="182" y2="452" stroke="#5b6566" stroke-width="3"/>
|
||||
<line x1="138" y1="452" x2="182" y2="452" stroke="#5b6566" stroke-width="3"/>
|
||||
|
||||
{{-- woda (zwierciadło dynamiczne) --}}
|
||||
<rect x="140" y="232" width="40" height="219" fill="#cfeaf5" opacity="0.65"/>
|
||||
{{-- zwierciadło statyczne Hst (przerywane) --}}
|
||||
<line x1="140" y1="206" x2="180" y2="206" stroke="#1565c0" stroke-width="1.4" stroke-dasharray="4 3"/>
|
||||
<text x="188" y="209" font-size="10" fill="{{ $label }}">H<tspan baseline-shift="sub" font-size="7">st</tspan></text>
|
||||
{{-- zwierciadło dynamiczne Hd --}}
|
||||
<line x1="140" y1="232" x2="180" y2="232" stroke="#1565c0" stroke-width="2"/>
|
||||
<text x="188" y="235" font-size="10" fill="{{ $label }}">H<tspan baseline-shift="sub" font-size="7">d</tspan></text>
|
||||
|
||||
{{-- rurociąg pionowy (kolor = straty liniowe) --}}
|
||||
<rect x="156" y="122" width="8" height="262" fill="{{ $pipeColor }}"/>
|
||||
|
||||
{{-- ===== Agregat pompowy ===== --}}
|
||||
{{-- pompa --}}
|
||||
<rect x="150" y="384" width="20" height="38" rx="3" fill="{{ $pumpColor }}" stroke="#2f3e45" stroke-width="1.5"/>
|
||||
{{-- silnik --}}
|
||||
<rect x="152" y="422" width="16" height="22" rx="2" fill="#46555d"/>
|
||||
{{-- króciec ssawny --}}
|
||||
<line x1="160" y1="444" x2="160" y2="450" stroke="#46555d" stroke-width="3"/>
|
||||
{{-- ciśnienie tłoczne pompy Pp --}}
|
||||
<circle cx="160" cy="372" r="7" fill="#ffffff" stroke="#3b3b3b" stroke-width="1.3"/>
|
||||
<text x="172" y="362" font-size="10" fill="{{ $label }}">P<tspan baseline-shift="sub" font-size="7">p</tspan></text>
|
||||
|
||||
{{-- etykieta typu + opis (prawa strona) --}}
|
||||
<text x="214" y="150" font-size="13" font-weight="700" fill="{{ $label }}">{{ $type }}</text>
|
||||
<text x="214" y="170" font-size="9" fill="{{ $label }}">Układ mierzy:</text>
|
||||
<text x="214" y="182" font-size="9" fill="{{ $label }}">p₁, p₂, P<tspan baseline-shift="sub" font-size="6.5">p</tspan>, Q,</text>
|
||||
<text x="214" y="194" font-size="9" fill="{{ $label }}">położenie zwierciadła:</text>
|
||||
<text x="214" y="206" font-size="9" fill="{{ $label }}">H<tspan baseline-shift="sub" font-size="6.5">d</tspan>, H<tspan baseline-shift="sub" font-size="6.5">st</tspan></text>
|
||||
</svg>
|
||||
@@ -0,0 +1,25 @@
|
||||
@props([
|
||||
'name',
|
||||
'label',
|
||||
'options', // kolekcja: [klucz => etykieta] lub lista etykiet (gdy valueIsLabel)
|
||||
'selected' => null,
|
||||
'required' => false,
|
||||
'placeholder' => null,
|
||||
'valueIsLabel' => false, // true → value = etykieta (słowniki opisowe: WLL_SYS_TYPE itp.)
|
||||
])
|
||||
|
||||
<div {{ $attributes->only('class')->merge(['class' => 'space-y-1']) }}>
|
||||
<label for="{{ $name }}" class="font-data-label text-data-label text-on-surface-variant tracking-wide">
|
||||
{{ $label }}@if ($required) <span class="text-primary">*</span>@endif
|
||||
</label>
|
||||
<select id="{{ $name }}" name="{{ $name }}" @required($required)
|
||||
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-data-label text-on-surface transition-all">
|
||||
@if ($placeholder)
|
||||
<option value="">{{ $placeholder }}</option>
|
||||
@endif
|
||||
@foreach ($options as $key => $text)
|
||||
@php($optionValue = $valueIsLabel ? $text : $key)
|
||||
<option value="{{ $optionValue }}" @selected((string) old($name, $selected) === (string) $optionValue)>{{ $text }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
@@ -0,0 +1,44 @@
|
||||
<x-app-layout :active="'dashboard'" title="SoftSPM 2 - Mapa Obiektów">
|
||||
<div class="space-y-stack-gap">
|
||||
<div>
|
||||
<h1 class="font-headline-lg text-headline-lg text-on-surface">Mapa Obiektów</h1>
|
||||
<p class="font-body-sm text-body-sm text-on-surface-variant mt-1">
|
||||
Zalogowano jako <span class="font-data-label">{{ auth()->user()?->USR_USERNAME }}</span>
|
||||
· Oddział: <span class="font-data-label">{{ session('department') ?? '-' }}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{{-- Mapa: studnie naniesione wg lokalnych współrzędnych geodezyjnych (siatka kopalniana). --}}
|
||||
<div class="relative rounded-xl overflow-hidden min-h-[74vh] shadow-card border border-outline-variant/30 bg-surface-container">
|
||||
{{-- Podkład - zdjęcie wg motywu (przyciemnione, dla efektu) --}}
|
||||
<img src="{{ asset('images/dashboard_01.png') }}" alt="" class="absolute inset-0 w-full h-full object-cover opacity-50 dark:hidden">
|
||||
<img src="{{ asset('images/dashboard_02.png') }}" alt="" class="absolute inset-0 w-full h-full object-cover opacity-60 hidden dark:block">
|
||||
<div class="absolute inset-0 bg-on-surface/20 dark:bg-black/45"></div>
|
||||
{{-- Subtelna siatka (czyta się jak plan/mapa) --}}
|
||||
<div class="absolute inset-0 opacity-[0.18]"
|
||||
style="background-image: linear-gradient(rgba(255,255,255,.5) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.5) 1px, transparent 1px); background-size: 48px 48px;"></div>
|
||||
|
||||
{{-- Kropki studni --}}
|
||||
@forelse ($points as $p)
|
||||
<a href="{{ route('well-operation.show', $p->drs_id) }}"
|
||||
aria-label="Studnia {{ $p->name }} - {{ $p->on ? 'pompa pracuje' : 'pompa nie pracuje' }}"
|
||||
style="left: {{ $p->left }}%; top: {{ $p->top }}%"
|
||||
class="group absolute z-20 -translate-x-1/2 -translate-y-1/2">
|
||||
<span class="block w-3 h-3 rounded-full ring-2 ring-white/80 shadow-md transition-transform group-hover:scale-150 {{ $p->on ? 'bg-green-500' : 'bg-red-500' }}"></span>
|
||||
<span class="pointer-events-none absolute left-1/2 bottom-full mb-1.5 -translate-x-1/2 px-2 py-0.5 rounded bg-on-surface text-inverse-on-surface font-data-label text-[11px] whitespace-nowrap opacity-0 group-hover:opacity-100 transition-opacity z-40">{{ $p->name }}</span>
|
||||
</a>
|
||||
@empty
|
||||
<div class="absolute inset-0 grid place-items-center z-20">
|
||||
<p class="font-body-md text-body-md text-white/90 bg-on-surface/50 px-4 py-2 rounded-lg">Brak studni z poprawnymi współrzędnymi dla tego oddziału.</p>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
{{-- Legenda pod mapą --}}
|
||||
<div class="flex flex-wrap items-center gap-x-6 gap-y-2 px-card-inner-padding py-3 bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card font-body-sm text-body-sm">
|
||||
<span class="font-title-sm text-title-sm text-on-surface">Studnie w eksploatacji: <span class="font-data-label">{{ $points->count() }}</span></span>
|
||||
<span class="flex items-center gap-2"><span class="w-3 h-3 rounded-full bg-green-500 ring-2 ring-white/70"></span> Pompa pracuje <span class="font-data-label text-on-surface-variant">({{ $onCount }})</span></span>
|
||||
<span class="flex items-center gap-2"><span class="w-3 h-3 rounded-full bg-red-500 ring-2 ring-white/70"></span> Pompa nie pracuje <span class="font-data-label text-on-surface-variant">({{ $offCount }})</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,91 @@
|
||||
<x-app-layout :active="'well-operation.index'" title="SoftSPM 2 - Eksploatacja studni">
|
||||
<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>
|
||||
<h1 class="font-headline-lg text-headline-lg text-on-surface">Eksploatacja studni</h1>
|
||||
<p class="font-body-sm text-body-sm text-on-surface-variant mt-1">
|
||||
Oddział: <span class="font-data-label">{{ $department ?? '-' }}</span>
|
||||
· Wyników: <span class="font-data-label">{{ $wells->total() }}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{{-- Wyszukiwarka --}}
|
||||
<form method="GET" action="{{ route('well-operation.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">Studnia</label>
|
||||
<input id="q" name="q" type="text" value="{{ $search }}" placeholder="nazwa studni"
|
||||
class="bg-surface-container-low border border-outline-variant px-3 py-2 rounded focus:ring-1 focus:ring-primary focus:border-primary font-data-label text-on-surface">
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<label for="status" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Status</label>
|
||||
<select id="status" name="status"
|
||||
class="bg-surface-container-low border border-outline-variant px-3 py-2 rounded focus:ring-1 focus:ring-primary focus:border-primary font-data-label text-on-surface">
|
||||
@foreach ($statusOptions as $id => $label)
|
||||
<option value="{{ $id }}" {{ (int) $status === (int) $id ? 'selected' : '' }}>{{ $label }}</option>
|
||||
@endforeach
|
||||
<option value="{{ $statusAll }}" {{ (int) $status === $statusAll ? 'selected' : '' }}>Wszystkie</option>
|
||||
</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>
|
||||
|
||||
{{-- Lista studni w eksploatacji --}}
|
||||
<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">Studnia</x-sortable-th>
|
||||
<x-sortable-th column="bariera">Ujęcie / Bariera</x-sortable-th>
|
||||
<x-sortable-th column="pump">Pompa</x-sortable-th>
|
||||
<x-sortable-th column="pump_no">Nr fabryczny pompy [S/N]</x-sortable-th>
|
||||
<x-sortable-th column="engine">Silnik</x-sortable-th>
|
||||
<x-sortable-th column="engine_no">Nr fabryczny silnika [S/N]</x-sortable-th>
|
||||
<x-sortable-th column="type">Typ</x-sortable-th>
|
||||
<th class="px-4 py-3 text-center">Stan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-outline-variant/30 font-body-md text-body-md">
|
||||
@forelse ($wells as $well)
|
||||
<tr class="hover:bg-surface-container transition-colors cursor-pointer"
|
||||
onclick="location.href='{{ route('well-operation.show', $well->drs_id) }}'">
|
||||
<td class="px-4 py-3 font-data-label text-on-surface-variant">{{ $wells->firstItem() + $loop->index }}.</td>
|
||||
<td class="px-4 py-3 font-semibold text-on-surface">{{ $well->well_name }}</td>
|
||||
<td class="px-4 py-3">{{ $well->bariera }}</td>
|
||||
<td class="px-4 py-3">{{ $well->pump_name }}</td>
|
||||
<td class="px-4 py-3 font-data-label">{{ $well->pump_no }}</td>
|
||||
<td class="px-4 py-3">{{ $well->engine_name }}</td>
|
||||
<td class="px-4 py-3 font-data-label">{{ $well->engine_no }}</td>
|
||||
<td class="px-4 py-3">{{ $well->sys_type }}</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
@if ($well->onoff === 'ON')
|
||||
<span class="status-chip status-chip-ok">ON</span>
|
||||
@else
|
||||
<span class="status-chip status-chip-alert">OFF</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="9" class="px-4 py-8 text-center font-body-md text-body-md text-on-surface-variant">
|
||||
Brak studni dla wybranych kryteriów.
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>{{ $wells->links() }}</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,247 @@
|
||||
<x-app-layout :active="'well-operation.index'" title="SoftSPM 2 - Parametry: {{ $well->well_name }}">
|
||||
@php
|
||||
$fmt = fn ($v, $d = 2) => $v === null || $v === '' ? '-' : number_format(round((float) $v, $d), $d, ',', ' ');
|
||||
$iv = fn ($v, $d) => ($v === null || $v === '') ? '' : number_format(round((float) $v, $d), $d, ',', '');
|
||||
// <option> nie renderuje HTML - zamiana sup/sub na unicode, reszta tagów usuwana.
|
||||
$optClean = fn ($s) => strip_tags(strtr((string) $s, [
|
||||
'<sup>2</sup>' => '²', '<sup>3</sup>' => '³',
|
||||
'<sub>0</sub>' => '₀', '<sub>1</sub>' => '₁', '<sub>2</sub>' => '₂',
|
||||
]));
|
||||
$isOn = $onoff && $onoff->DRD_VALUE === 'ON';
|
||||
$qMin = $measurement && $measurement->q !== null ? (float) $measurement->q / 60 : null; // m³/min
|
||||
|
||||
// Pola pomiaru: [name, etykieta(html), wartość, miejsca dziesiętne].
|
||||
$pf = [
|
||||
['q', 'Wydajność Q [m³/min]', $qMin, 3],
|
||||
['hd', 'Zwierciadło dynamiczne H<sub>D</sub> [m]', $measurement?->hd, 2],
|
||||
['hst', 'Zwierciadło statyczne H<sub>ST</sub> [m]', $measurement?->hst, 2],
|
||||
['hst0', 'Zwierciadło statyczne H<sub>ST0</sub> [m]', $measurement?->hst0, 1],
|
||||
['p1', 'Ciśnienie p<sub>1</sub> [m]', $measurement?->p1, 2],
|
||||
['p2', 'Ciśnienie p<sub>2</sub> [m]', $measurement?->p2, 2],
|
||||
['pb', 'Ciśnienie barometryczne p<sub>b</sub> [hPa]', $extra->pb, 2],
|
||||
['vm3', 'Ilość wypompowanej wody V [m³]', $measurement?->vm3, 2],
|
||||
['wtemp', 'Temp. wody T<sub>W</sub> [°C]', $measurement?->wtemp, 1],
|
||||
['pp', 'Ciśnienie p<sub>p</sub> [m]', $measurement?->pp, 2],
|
||||
['hh', 'Wysokość H<sub>H</sub> [m]', $measurement?->hh, 2],
|
||||
['throttle', 'Otwarcie przepustnicy [%]', $extra->throttle, 1],
|
||||
'---',
|
||||
['pe', 'Moc czynna P<sub>e</sub> [kW]', $measurement?->pe, 2],
|
||||
['energy_kwh', 'Pobór energii E [kWh]', $extra->energy_kwh, 2],
|
||||
['i', 'Pobór prądu I [A]', $measurement?->i, 2],
|
||||
['u', 'Napięcie zasilania U [V]', $measurement?->u, 0],
|
||||
['cos', 'cos(φ)', $measurement?->cos, 3],
|
||||
['work_time', 'Czas pracy agregatu t [h]', $measurement?->work_time, 2],
|
||||
['unit_energy', 'Jednostkowe zużycie energii [Wh/m³/m]', $extra->unit_energy, 2],
|
||||
];
|
||||
$inputClass = 'param-input w-32 ml-auto block text-right font-data-label text-on-surface rounded border px-2 py-0.5 bg-surface-container-low border-outline-variant read-only:border-transparent read-only:bg-transparent read-only:px-0 read-only:cursor-default focus:outline-none focus:ring-1 focus:ring-primary';
|
||||
|
||||
// Przyciski nawigacyjne (podstrony 401). Tylko „Ocena pracy układu" zbudowana.
|
||||
$nav = [
|
||||
['Ocena pracy układu', route('well-operation.show', $well->drs_id), true],
|
||||
['Historia ocen', null, false],
|
||||
['Historia agregatów', null, false],
|
||||
['Historia postojów', null, false],
|
||||
['Karta otworu', null, false],
|
||||
['Parametry chemiczne', null, false],
|
||||
['Stan studni', null, false],
|
||||
['Kalibracja', null, false],
|
||||
];
|
||||
@endphp
|
||||
|
||||
<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
|
||||
@if ($errors->any())
|
||||
<div class="status-chip-alert px-3 py-2 rounded-lg font-body-sm text-body-sm">{{ $errors->first() }}</div>
|
||||
@endif
|
||||
|
||||
{{-- Powrót do oceny pracy układu --}}
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('well-operation.show', $well->drs_id) }}" class="font-body-sm text-body-sm text-on-surface-variant hover:text-primary">← Ocena pracy układu</a>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-stack-gap items-start">
|
||||
|
||||
{{-- ===== LEWA: studnia + parametry techniczne + pracy ===== --}}
|
||||
<div class="space-y-stack-gap">
|
||||
|
||||
{{-- Studnia --}}
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="bg-secondary text-on-secondary px-card-inner-padding py-2 font-title-sm text-title-sm">Studnia</div>
|
||||
<div class="py-4 text-center font-headline-lg text-headline-lg {{ $isOn ? 'text-green-600' : 'text-on-surface' }}">{{ $well->well_name }}</div>
|
||||
</div>
|
||||
|
||||
{{-- Data ostatniej aktualizacji danych z SYNDIS --}}
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="bg-secondary text-on-secondary px-card-inner-padding py-2 font-title-sm text-title-sm">Data ostatniej aktualizacji danych z SYNDIS</div>
|
||||
<div class="py-3 text-center font-headline-md text-headline-md text-secondary">{{ $measurement->date ?? '-' }}</div>
|
||||
</div>
|
||||
|
||||
{{-- Parametry techniczne studni --}}
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="bg-secondary text-on-secondary px-card-inner-padding py-2 font-title-sm text-title-sm">Parametry techniczne studni</div>
|
||||
<table class="w-full font-body-md text-body-md">
|
||||
<tbody class="divide-y divide-outline-variant/30">
|
||||
@php
|
||||
$tech = collect([
|
||||
['Nazwa', $well->well_name],
|
||||
['Kategoria SPM', $category ?: '-'],
|
||||
['Data zabudowy pompy', $availableDate ?: '-'],
|
||||
]);
|
||||
foreach ($techParams as $tp) { $tech->push([$tp->label, $tp->value ?: '-']); }
|
||||
@endphp
|
||||
@foreach ($tech as [$label, $value])
|
||||
<tr>
|
||||
<td class="px-4 py-1.5 w-1/2 bg-surface-container/50 text-on-surface-variant font-data-label text-data-label">{!! $label !!}</td>
|
||||
<td class="px-4 py-1.5">{{ $value }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
<tr><td class="px-4 py-1.5 bg-surface-container/50 text-on-surface-variant font-data-label text-data-label">Głębokość zabudowy pompy L<sub>P</sub> [m]</td><td class="px-4 py-1.5 font-data-label">{{ $fmt($lp, 2) }}</td></tr>
|
||||
<tr><td class="px-4 py-1.5 bg-surface-container/50 text-on-surface-variant font-data-label text-data-label">Średnica rurociągu D<sub>R</sub> [mm]</td><td class="px-4 py-1.5 font-data-label">{{ $dr === null ? '-' : $fmt((float) $dr * 1000, 0) }}</td></tr>
|
||||
<tr><td class="px-4 py-1.5 bg-surface-container/50 text-on-surface-variant font-data-label text-data-label">m [m]</td><td class="px-4 py-1.5 font-data-label">{{ $fmt($mParam, 2) }}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{-- Parametry pracy (z ręczną edycją) --}}
|
||||
<form id="measForm" method="POST" action="{{ route('well-operation.params-store', $well->drs_id) }}">
|
||||
@csrf
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="bg-secondary text-on-secondary px-card-inner-padding py-2 font-title-sm text-title-sm flex items-center justify-between gap-2">
|
||||
<span>Parametry pracy studni i układu pompowego</span>
|
||||
<span class="flex items-center gap-2">
|
||||
<button type="button" id="measEdit" title="Wprowadź ręcznie pomiar"
|
||||
class="px-2.5 py-1 rounded-full bg-on-secondary/15 hover:bg-on-secondary/25 font-data-label text-data-label">✎ Modyfikuj</button>
|
||||
<span id="measSave" class="hidden items-center gap-2">
|
||||
<button type="submit" class="px-2.5 py-1 rounded-full bg-on-secondary text-secondary font-data-label text-data-label hover:opacity-90">Zapisz</button>
|
||||
<button type="button" onclick="location.reload()" class="px-2.5 py-1 rounded-full bg-on-secondary/15 font-data-label text-data-label hover:bg-on-secondary/25">Anuluj</button>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<table class="w-full font-body-md text-body-md">
|
||||
<tbody class="divide-y divide-outline-variant/30">
|
||||
<tr>
|
||||
<td class="px-4 py-1.5 w-2/3 bg-surface-container/50 text-on-surface-variant font-data-label text-data-label">Data pomiaru</td>
|
||||
<td class="px-4 py-1 text-right">
|
||||
<div class="flex items-center justify-end gap-1">
|
||||
<input type="date" name="date" value="{{ now()->format('Y-m-d') }}" readonly class="{{ $inputClass }} w-36">
|
||||
<input type="time" name="time" value="{{ now()->format('H:i') }}" readonly class="{{ $inputClass }} w-24">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@foreach ($pf as $row)
|
||||
@if ($row === '---')
|
||||
<tr><td colspan="2" class="px-4 py-1 bg-surface-container-low"></td></tr>
|
||||
@else
|
||||
<tr>
|
||||
<td class="px-4 py-1.5 w-2/3 bg-surface-container/50 text-on-surface-variant font-data-label text-data-label">{!! $row[1] !!}</td>
|
||||
<td class="px-4 py-1 text-right">
|
||||
<input type="text" name="{{ $row[0] }}" value="{{ $iv($row[2], $row[3]) }}" readonly class="{{ $inputClass }}">
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{-- ===== PRAWA: bilans mocy + rysunek układu ===== --}}
|
||||
<div class="space-y-stack-gap">
|
||||
|
||||
{{-- Bilans mocy --}}
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="bg-secondary text-on-secondary px-card-inner-padding py-2 font-title-sm text-title-sm">Bilans mocy</div>
|
||||
@if ($power)
|
||||
<table class="w-full font-body-md text-body-md">
|
||||
<tbody class="divide-y divide-outline-variant/30">
|
||||
<tr><td colspan="2" class="px-4 py-1.5 bg-surface-container text-center font-semibold text-on-surface">Moc przekazana</td></tr>
|
||||
<tr><td class="px-4 py-1.5 w-2/3 text-on-surface-variant">Podnoszenie stałe</td><td class="px-4 py-1.5 text-right font-data-label">{{ $fmt($power['pst']) }} [kW]</td></tr>
|
||||
<tr><td class="px-4 py-1.5 text-on-surface-variant">Geo - hydraulika</td><td class="px-4 py-1.5 text-right font-data-label">{{ $fmt($power['pgh']) }} [kW]</td></tr>
|
||||
<tr><td class="px-4 py-1.5 text-on-surface-variant">Odpływ</td><td class="px-4 py-1.5 text-right font-data-label">{{ $fmt($power['pp2']) }} [kW]</td></tr>
|
||||
<tr><td colspan="2" class="px-4 py-1.5 bg-surface-container text-center font-semibold text-on-surface">Moc stracona</td></tr>
|
||||
<tr><td class="px-4 py-1.5 text-on-surface-variant">Agregat + zasilanie</td><td class="px-4 py-1.5 text-right font-data-label">{{ $fmt($power['paz']) }} [kW]</td></tr>
|
||||
<tr><td class="px-4 py-1.5 text-on-surface-variant">Straty liniowe</td><td class="px-4 py-1.5 text-right font-data-label">{{ $fmt($power['psl']) }} [kW]</td></tr>
|
||||
<tr><td class="px-4 py-1.5 text-on-surface-variant">Straty w armaturze</td><td class="px-4 py-1.5 text-right font-data-label">{{ $fmt($power['psa']) }} [kW]</td></tr>
|
||||
<tr><td colspan="2" class="px-2 py-1"></td></tr>
|
||||
<tr><td class="px-4 py-1.5 text-on-surface-variant">Moc pobrana P<sub>e</sub></td><td class="px-4 py-1.5 text-right font-data-label font-semibold">{{ $fmt($power['pe']) }} [kW]</td></tr>
|
||||
<tr><td class="px-4 py-1.5 text-on-surface-variant">Moc zużyta Σ</td><td class="px-4 py-1.5 text-right font-data-label font-semibold">{{ $fmt($power['sum']) }} [kW]</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<p class="p-card-inner-padding font-body-sm text-body-sm text-on-surface-variant">Brak danych pomiarowych do bilansu mocy.</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Układ pompowy (rysunek rozkładu mocy) --}}
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="bg-secondary text-on-secondary px-card-inner-padding py-2 font-title-sm text-title-sm">Układ pompowy - {{ $well->well_name }}</div>
|
||||
<div class="p-card-inner-padding">
|
||||
<div class="rounded border border-outline-variant/40 p-2" style="background:#f7fafb">
|
||||
<x-power-schematic :power="$power" :on="$isOn" :time="$measurement?->date" />
|
||||
</div>
|
||||
<p class="mt-2 font-body-sm text-body-sm text-on-surface-variant">
|
||||
Pompa: <span class="font-semibold text-on-surface">{{ $well->pump_name ?: '-' }}</span>
|
||||
@if ($well->pump_no) <span class="font-data-label">({{ $well->pump_no }})</span> @endif
|
||||
· Silnik: <span class="font-semibold text-on-surface">{{ $well->engine_name ?: '-' }}</span>
|
||||
@if ($well->engine_no) <span class="font-data-label">({{ $well->engine_no }})</span> @endif
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Historia po zadanym parametrze --}}
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="bg-secondary text-on-secondary px-card-inner-padding py-2 font-title-sm text-title-sm">Historia po zadanym parametrze</div>
|
||||
<div class="p-card-inner-padding flex flex-wrap items-end gap-3">
|
||||
<div class="space-y-1">
|
||||
<label for="histparam" class="font-data-label text-data-label text-on-surface-variant tracking-wide">Wybierz parametr</label>
|
||||
<select id="histparam" class="bg-surface-container-low border border-outline-variant px-3 py-2 rounded font-data-label text-on-surface">
|
||||
<option value="0">Wybierz z listy</option>
|
||||
@foreach ($historyParams as $id => $label)
|
||||
<option value="{{ $id }}">{{ $optClean($label) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<x-btn :disabled="true" title="W przygotowaniu (historia parametru)">Pokaż</x-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Przyciski nawigacyjne (podstrony) - maks. 2 w rzędzie --}}
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
||||
@foreach ($nav as [$label, $href, $active])
|
||||
@if ($active)
|
||||
<x-btn as="a" href="{{ $href }}" class="w-full">{{ $label }}</x-btn>
|
||||
@else
|
||||
<x-btn :disabled="true" title="W przygotowaniu" class="w-full">{{ $label }}</x-btn>
|
||||
@endif
|
||||
@endforeach
|
||||
<x-btn :disabled="true" title="W przygotowaniu" class="w-full sm:col-span-2">Pomiary na żądanie</x-btn>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Toggle ręcznej edycji pomiaru --}}
|
||||
<script>
|
||||
(function () {
|
||||
const eb = document.getElementById('measEdit');
|
||||
const sv = document.getElementById('measSave');
|
||||
const fm = document.getElementById('measForm');
|
||||
if (! eb || ! fm) return;
|
||||
eb.addEventListener('click', function () {
|
||||
fm.querySelectorAll('.param-input').forEach(i => i.removeAttribute('readonly'));
|
||||
eb.classList.add('hidden');
|
||||
sv.classList.remove('hidden');
|
||||
sv.classList.add('flex');
|
||||
const q = fm.querySelector('[name="q"]');
|
||||
if (q) q.focus();
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,168 @@
|
||||
<x-app-layout :active="'well-operation.index'" title="SoftSPM 2 - {{ $well->well_name }}">
|
||||
@php
|
||||
$fmt = fn ($v, $d = 2) => $v === null || $v === '' ? '-' : number_format(round((float) $v, $d), $d, ',', ' ');
|
||||
// Klasy komórki oceny wg strefy.
|
||||
$zoneCell = [
|
||||
'green' => 'bg-green-100 text-green-900 dark:bg-green-900/30 dark:text-green-200',
|
||||
'yellow' => 'bg-amber-100 text-amber-900 dark:bg-amber-900/30 dark:text-amber-200',
|
||||
'red' => 'bg-red-100 text-red-900 dark:bg-red-900/30 dark:text-red-200',
|
||||
];
|
||||
$zoneHex = ['green' => '#2e7d32', 'yellow' => '#f9a825', 'red' => '#c62828'];
|
||||
// Kolor punktu pracy na wykresie Hp = ocena H=f(Q) (jak Evaluation4_HfQ w legacy).
|
||||
$hfqZone = $assessments['hfq']['zone'] ?? null;
|
||||
$hfqColor = $hfqZone ? $zoneHex[$hfqZone] : '#5b6566';
|
||||
$spmType = 'SPM - '.strtoupper(str_replace(['images/spm_', '.png'], '', $spmImage));
|
||||
$rows = [
|
||||
['Przedział Q', $assessments['q'] ?? null],
|
||||
['H = f(Q)', $assessments['hfq'] ?? null],
|
||||
['Dławienie', $assessments['throttle'] ?? null],
|
||||
['Straty liniowe', $assessments['loss'] ?? null],
|
||||
];
|
||||
$isOn = $onoff && $onoff->DRD_VALUE === 'ON';
|
||||
@endphp
|
||||
|
||||
<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
|
||||
|
||||
{{-- Nagłówek --}}
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('well-operation.index') }}" class="font-body-sm text-body-sm text-on-surface-variant hover:text-primary">← Eksploatacja studni</a>
|
||||
<h1 class="font-headline-lg text-headline-lg text-on-surface">{{ $well->well_name }}</h1>
|
||||
</div>
|
||||
@if ($isOn)
|
||||
<span class="status-chip status-chip-ok">Załączona</span>
|
||||
@else
|
||||
<span class="status-chip status-chip-alert">Wyłączona</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-stack-gap items-start">
|
||||
|
||||
{{-- ===== LEWA KOLUMNA ===== --}}
|
||||
<div class="space-y-stack-gap">
|
||||
|
||||
{{-- Oceny pracy układu pompowego --}}
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="bg-secondary text-on-secondary px-card-inner-padding py-2 font-title-sm text-title-sm">Oceny pracy układu pompowego</div>
|
||||
@if ($assessments === null)
|
||||
<p class="p-card-inner-padding font-body-sm text-body-sm text-on-surface-variant">Studnia typu „SPM - inne" - brak automatycznej oceny.</p>
|
||||
@else
|
||||
<table class="w-full font-body-md text-body-md">
|
||||
<tbody class="divide-y divide-outline-variant/30">
|
||||
@foreach ($rows as [$label, $a])
|
||||
<tr>
|
||||
<td class="px-4 py-2 w-40 bg-surface-container/60 text-on-surface-variant font-data-label text-data-label align-top">{{ $label }}</td>
|
||||
<td class="px-4 py-2 {{ $a ? $zoneCell[$a['zone']] : 'text-on-surface-variant' }}">{{ $a['text'] ?? 'Brak oceny' }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Układ pompowy + schemat SPM --}}
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="bg-secondary text-on-secondary px-card-inner-padding py-2 font-title-sm text-title-sm">Układ pompowy - {{ $well->well_name }}</div>
|
||||
<div class="p-card-inner-padding space-y-3">
|
||||
<dl class="grid grid-cols-3 gap-x-4 gap-y-1.5 font-body-md text-body-md">
|
||||
<dt class="text-on-surface-variant">Ujęcie / Bariera</dt><dd class="col-span-2">{{ $well->bariera ?: '-' }}</dd>
|
||||
<dt class="text-on-surface-variant">Pompa</dt><dd class="col-span-2 font-semibold">{{ $well->pump_name ?: '-' }} <span class="font-data-label text-on-surface-variant">{{ $well->pump_no ? '· '.$well->pump_no : '' }}</span></dd>
|
||||
<dt class="text-on-surface-variant">Silnik</dt><dd class="col-span-2 font-semibold">{{ $well->engine_name ?: '-' }} <span class="font-data-label text-on-surface-variant">{{ $well->engine_no ? '· '.$well->engine_no : '' }}</span></dd>
|
||||
<dt class="text-on-surface-variant">Q<sub>n</sub> [m³/min]</dt><dd class="col-span-2 font-data-label">{{ $fmt($qnMin, 3) }}</dd>
|
||||
</dl>
|
||||
|
||||
<div class="relative rounded border border-outline-variant/40 p-2" style="background:#f7fafb">
|
||||
<x-well-schematic
|
||||
:throttle="$assessments['throttle']['zone'] ?? null"
|
||||
:loss="$assessments['loss']['zone'] ?? null"
|
||||
:on="$isOn"
|
||||
:grey="$grey"
|
||||
:type="$spmType" />
|
||||
{{-- Status diagnostyki --}}
|
||||
<span class="absolute top-3 right-3 inline-flex items-center gap-1 px-2 py-0.5 rounded-full font-data-label text-data-label
|
||||
@if ((int) $diag === 2) bg-red-100 text-red-800 @elseif ((int) $diag === 1) bg-amber-100 text-amber-800 @else bg-green-100 text-green-800 @endif">
|
||||
@if ((int) $diag === 2) Diagnostyka: błąd @elseif ((int) $diag === 1) Diagnostyka: ostrzeżenie @else Diagnostyka: OK @endif
|
||||
</span>
|
||||
</div>
|
||||
@if ($measurement)
|
||||
<p class="text-center font-data-label text-data-label text-secondary">Data odpytania SYNDIS: {{ $measurement->date }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Stan pracy --}}
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="bg-secondary text-on-secondary px-card-inner-padding py-2 font-title-sm text-title-sm">
|
||||
Stan pracy - {{ $isOn ? 'Załączona' : 'Wyłączona' }}
|
||||
@if ($onoff) <span class="font-data-label text-data-label opacity-80">[{{ $onoff->DRD_AUDIT_DATE }}]</span> @endif
|
||||
</div>
|
||||
<div class="p-card-inner-padding flex items-center gap-4">
|
||||
<div class="grid place-items-center w-28 h-14 rounded-lg font-headline-md text-headline-md {{ $isOn ? 'bg-green-600 text-white' : 'bg-surface-container text-on-surface-variant/50' }}">ON</div>
|
||||
<div class="grid place-items-center w-28 h-14 rounded-lg font-headline-md text-headline-md {{ ! $isOn ? 'bg-red-600 text-white' : 'bg-surface-container text-on-surface-variant/50' }}">OFF</div>
|
||||
@if ($onoff && ! empty($onoff->reason))
|
||||
<div class="font-body-sm text-body-sm text-on-surface-variant">
|
||||
Powód: <span class="font-semibold text-on-surface">{{ $onoff->reason }}</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Akcje - pod stanem pracy (siatka 2-kol, jak na stronie parametrów) --}}
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2 pt-1">
|
||||
<x-btn as="a" href="{{ route('well-operation.params', $well->drs_id) }}" class="w-full">Parametry pracy układu</x-btn>
|
||||
<form method="POST" action="{{ route('well-operation.fetch', $well->drs_id) }}">
|
||||
@csrf
|
||||
<x-btn type="submit" class="w-full">Pobierz aktualne dane</x-btn>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ===== PRAWA KOLUMNA ===== --}}
|
||||
<div class="space-y-stack-gap">
|
||||
|
||||
{{-- Ocena energochłonności --}}
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="bg-secondary text-on-secondary px-card-inner-padding py-2 font-title-sm text-title-sm">Ocena energochłonności</div>
|
||||
<div class="p-card-inner-padding">
|
||||
@if ($energy)
|
||||
<x-energy-gauge :eta="$energy['eta']" :ry="$energy['ry']" :yg="$energy['yg']" :zone="$energy['zone']" />
|
||||
@else
|
||||
<div class="h-12 rounded bg-surface-container grid place-items-center font-body-sm text-body-sm text-on-surface-variant">Brak oceny</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Wysokość podnoszenia --}}
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="bg-secondary text-on-secondary px-card-inner-padding py-2 font-title-sm text-title-sm">Wysokość podnoszenia</div>
|
||||
<div class="p-card-inner-padding">
|
||||
@if ($hPlot)
|
||||
<x-pump-curve :plot="$hPlot" x-label="Q [m³/min]" y-label="H [m]" :op-x="$opQ" :op-y="$opH" :op-color="$hfqColor" op-y-label="H" />
|
||||
@else
|
||||
<p class="py-8 text-center font-body-sm text-body-sm text-on-surface-variant">Brak danych próby pompy.</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Sprawność --}}
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card overflow-hidden">
|
||||
<div class="bg-secondary text-on-secondary px-card-inner-padding py-2 font-title-sm text-title-sm">Sprawność</div>
|
||||
<div class="p-card-inner-padding">
|
||||
@if ($etaPlot)
|
||||
<x-pump-curve :plot="$etaPlot" x-label="Q [m³/min]" y-label="η [-]" :op-x="$opQ" :op-y="$opEta" op-color="#5b6566" :band="true" />
|
||||
@else
|
||||
<p class="py-8 text-center font-body-sm text-body-sm text-on-surface-variant">Brak danych próby pompy.</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,15 @@
|
||||
@php($sec = collect(config('navigation'))->firstWhere('key', $sectionKey))
|
||||
|
||||
<x-app-layout :section="$sectionKey" :title="'SoftSPM 2 - '.($sec['label'] ?? '')">
|
||||
<div class="space-y-stack-gap">
|
||||
<h1 class="font-headline-lg text-headline-lg text-on-surface">{{ $sec['label'] ?? '' }}</h1>
|
||||
|
||||
<div class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card p-8 min-h-[40vh] flex items-center justify-center">
|
||||
<div class="text-center">
|
||||
<p class="font-body-md text-body-md text-on-surface-variant">
|
||||
Wybierz pozycję z menu po lewej stronie.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,48 @@
|
||||
<x-app-layout :active="'wells.index'" title="SoftSPM 2 - Nowa studnia">
|
||||
<div class="max-w-3xl space-y-stack-gap">
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('wells.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">Nowa studnia</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('wells.store') }}"
|
||||
class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card p-card-inner-padding space-y-4">
|
||||
@csrf
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<x-well-field name="name" label="Nazwa studni" :value="old('name')" required class="sm:col-span-2" />
|
||||
|
||||
<x-well-select name="water_intake_id" label="Ujęcie / bariera" :options="$waterIntakes" :selected="old('water_intake_id')" required placeholder="- wybierz -" />
|
||||
<x-well-select name="category_id" label="Kategoria" :options="$categories" :selected="old('category_id', 19)" required />
|
||||
|
||||
<x-well-field name="diameter" label="Średnica rurociągu" :value="old('diameter')" />
|
||||
<x-well-field name="depth" label="Głębokość studni" :value="old('depth')" />
|
||||
<x-well-field name="supply" label="Głębokość zasypu" :value="old('supply')" />
|
||||
<x-well-select name="status_id" label="Status" :options="$statuses" :selected="old('status_id')" required placeholder="- wybierz -" />
|
||||
|
||||
<x-well-field name="latitude" label="Szerokość geograficzna" :value="old('latitude')" />
|
||||
<x-well-field name="longitude" label="Długość geograficzna" :value="old('longitude')" />
|
||||
|
||||
<x-well-field name="available_date" label="Data oddania" type="date" :value="old('available_date')" />
|
||||
</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('wells.index') }}"
|
||||
class="px-5 py-2.5 rounded 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="'wells.index'" title="SoftSPM 2 - Edycja studni">
|
||||
@php
|
||||
// Data/godzina zabudowy z ostatniej wymiany agregatu (jak site301E).
|
||||
$dateVal = $well->wle_date_from ? substr($well->wle_date_from, 0, 10) : ($well->available_date ?? '');
|
||||
$timeVal = $well->wle_date_from ? substr($well->wle_date_from, 11, 5) : '';
|
||||
@endphp
|
||||
|
||||
<div class="max-w-3xl space-y-stack-gap">
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('wells.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">Edycja: {{ $well->name }}</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('wells.update', $well->drs_id) }}"
|
||||
class="bg-surface-container-lowest border border-outline-variant/40 rounded-lg shadow-card p-card-inner-padding space-y-4">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<input type="hidden" name="well_id" value="{{ $well->well_id }}">
|
||||
<input type="hidden" name="wle_id" value="{{ $well->wle_id }}">
|
||||
|
||||
{{-- Zabudowany agregat (tylko do odczytu) --}}
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<x-well-field name="_pump_name" label="Pompa" :value="$well->pump_name" readonly />
|
||||
<x-well-field name="_pump_no" label="Nr pompy" :value="$well->pump_no" readonly />
|
||||
<x-well-field name="_engine_name" label="Silnik" :value="$well->engine_name" readonly />
|
||||
<x-well-field name="_engine_no" label="Nr silnika" :value="$well->engine_no" readonly />
|
||||
</div>
|
||||
|
||||
<hr class="border-outline-variant/40">
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<x-well-field name="name" label="Nazwa studni" :value="$well->name" required class="sm:col-span-2" />
|
||||
|
||||
<x-well-select name="water_intake_id" label="Ujęcie / bariera" :options="$waterIntakes" :selected="$well->water_intake_id" required />
|
||||
<x-well-select name="category_id" label="Kategoria" :options="$categories" :selected="$well->category_id" required />
|
||||
<x-well-select name="status_id" label="Status" :options="$statuses" :selected="$well->status_id" required />
|
||||
|
||||
<x-well-field name="available_date" label="Data zabudowy" type="date" :value="$dateVal" />
|
||||
<x-well-field name="build_time" label="Godzina zabudowy" type="time" :value="$timeVal" />
|
||||
|
||||
<x-well-field name="latitude" label="Szerokość geograficzna" :value="$well->latitude" />
|
||||
<x-well-field name="longitude" label="Długość geograficzna" :value="$well->longitude" />
|
||||
|
||||
<x-well-select name="sys_type" label="Typ systemu" :options="$sysTypes" :selected="$well->sys_type" :value-is-label="true" placeholder="- wybierz -" />
|
||||
<x-well-select name="status_desc" label="Opis stanu" :options="$statusDescs" :selected="$well->status_desc" :value-is-label="true" placeholder="- wybierz -" />
|
||||
|
||||
<x-well-field name="pp_scope" label="Zakres ciśnienia p_p [m]" :value="$well->pp_scope" />
|
||||
<x-well-field name="hh_scope" label="Zakres sondy H_H [m]" :value="$well->hh_scope" />
|
||||
</div>
|
||||
|
||||
<p class="font-body-sm text-body-sm text-on-surface-variant">
|
||||
Uwaga: zakresy p<sub>p</sub>/H<sub>H</sub> są aktualizowane automatycznie po przypisaniu sondy SGP.
|
||||
Edytuj ręcznie tylko gdy wartość jest pusta.
|
||||
</p>
|
||||
|
||||
<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('wells.index') }}"
|
||||
class="px-5 py-2.5 rounded 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,109 @@
|
||||
<x-app-layout :active="'wells.index'" title="SoftSPM 2 - Studnie">
|
||||
<div class="space-y-stack-gap">
|
||||
|
||||
{{-- Komunikat sukcesu --}}
|
||||
@if (session('status'))
|
||||
<div class="status-chip-ok px-3 py-2 rounded-lg font-body-sm text-body-sm">
|
||||
{{ session('status') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Nagłówek --}}
|
||||
<div class="flex items-end justify-between">
|
||||
<div>
|
||||
<h1 class="font-headline-lg text-headline-lg text-on-surface">Studnie</h1>
|
||||
<p class="font-body-sm text-body-sm text-on-surface-variant mt-1">
|
||||
Oddział: <span class="font-data-label">{{ $department ?? '-' }}</span>
|
||||
· Wyników: <span class="font-data-label">{{ $wells->total() }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<a href="{{ route('wells.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 studnię
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{-- Wyszukiwarka (GET - filtry komponują się z paginacją) --}}
|
||||
<form method="GET" action="{{ route('wells.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 uppercase tracking-wider">Studnia</label>
|
||||
<input id="q" name="q" type="text" value="{{ $search }}"
|
||||
placeholder="nazwa studni"
|
||||
class="bg-surface-container-low border border-outline-variant px-3 py-2 rounded focus:ring-1 focus:ring-primary focus:border-primary font-data-label text-on-surface">
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<label for="status" class="font-data-label text-data-label text-on-surface-variant uppercase tracking-wider">Status</label>
|
||||
<select id="status" name="status"
|
||||
class="bg-surface-container-low border border-outline-variant px-3 py-2 rounded focus:ring-1 focus:ring-primary focus:border-primary font-data-label text-on-surface">
|
||||
@foreach ($statusOptions as $id => $label)
|
||||
<option value="{{ $id }}" {{ (int) $status === (int) $id ? 'selected' : '' }}>{{ $label }}</option>
|
||||
@endforeach
|
||||
<option value="{{ $statusAll }}" {{ (int) $status === $statusAll ? 'selected' : '' }}>Wszystkie</option>
|
||||
</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 uppercase tracking-wider">
|
||||
<th class="px-4 py-3 w-12">Lp.</th>
|
||||
<x-sortable-th column="name">Nazwa</x-sortable-th>
|
||||
<x-sortable-th column="bariera">Bariera</x-sortable-th>
|
||||
<x-sortable-th column="pump">Pompa</x-sortable-th>
|
||||
<x-sortable-th column="pump_no">Nr pompy</x-sortable-th>
|
||||
<x-sortable-th column="engine">Silnik</x-sortable-th>
|
||||
<x-sortable-th column="engine_no">Nr silnika</x-sortable-th>
|
||||
<x-sortable-th column="type">Typ</x-sortable-th>
|
||||
<th class="px-4 py-3 text-center">Stan</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 ($wells as $well)
|
||||
<tr class="hover:bg-surface-container transition-colors">
|
||||
<td class="px-4 py-3 font-data-label text-on-surface-variant">{{ $wells->firstItem() + $loop->index }}.</td>
|
||||
<td class="px-4 py-3 font-semibold text-on-surface">{{ $well->well_name }}</td>
|
||||
<td class="px-4 py-3">{{ $well->bariera }}</td>
|
||||
<td class="px-4 py-3">{{ $well->pump_name }}</td>
|
||||
<td class="px-4 py-3 font-data-label">{{ $well->pump_no }}</td>
|
||||
<td class="px-4 py-3">{{ $well->engine_name }}</td>
|
||||
<td class="px-4 py-3 font-data-label">{{ $well->engine_no }}</td>
|
||||
<td class="px-4 py-3">{{ $well->sys_type }}</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
@if ($well->onoff === 'ON')
|
||||
<span class="status-chip status-chip-ok">ON</span>
|
||||
@else
|
||||
<span class="status-chip bg-surface-container-high text-on-surface-variant">OFF</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
<a href="{{ route('wells.edit', $well->drs_id) }}"
|
||||
class="font-body-sm text-body-sm text-secondary hover:underline">Edytuj</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="10" class="px-4 py-8 text-center font-body-md text-body-md text-on-surface-variant">
|
||||
Brak studni dla wybranych kryteriów.
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{-- Paginacja --}}
|
||||
<div>
|
||||
{{ $wells->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
Reference in New Issue
Block a user