diff --git a/002.FRONTEND/src/components/RadniNalogLista.jsx b/002.FRONTEND/src/components/RadniNalogLista.jsx
deleted file mode 100644
index 1615676..0000000
--- a/002.FRONTEND/src/components/RadniNalogLista.jsx
+++ /dev/null
@@ -1,29 +0,0 @@
-// src/components/RadniNalogLista.jsx
-import { h } from 'preact';
-import { useEffect } from 'preact/hooks';
-import { useStore } from '@nanostores/preact';
-import { radniNalozi, setRadniNalozi } from '../stores/operativaStore';
-
-export default function RadniNalogLista({ nalozi = [], limit = 5 }) {
- useEffect(() => {
- if (nalozi.length > 0) setRadniNalozi(nalozi);
- }, [nalozi]);
-
- const podaciIzStora = useStore(radniNalozi);
-
- if (!podaciIzStora.length) return
Učitavanje...
;
-
- return (
-
+ {/* children funkcija sada prima item i opcionalni baseUrl */}
+ {displayList.map(item => children(item, baseUrl))}
+
+ );
+}
\ No newline at end of file
diff --git a/002.FRONTEND/src/components/edit/RadniNalogEditorStatus.jsx b/002.FRONTEND/src/components/edit/RadniNalogEditorStatus.jsx
deleted file mode 100644
index 6e54feb..0000000
--- a/002.FRONTEND/src/components/edit/RadniNalogEditorStatus.jsx
+++ /dev/null
@@ -1,31 +0,0 @@
-// src/components/edit/RadniNalogEditorStatus.jsx
-// Ovo postaje višak
-import { h } from 'preact';
-import { useState } from 'preact/hooks';
-import { patchNalog } from '../../lib/api';
-import Button from '../Button'; // Import nove komponente
-
-export default function RadniNalogEditorStatus({ nalogId, pocetniStatus }) {
- const [status, setStatus] = useState(pocetniStatus);
- const [loading, setLoading] = useState(false);
-
- const azurirajStatus = async () => {
- setLoading(true);
- const rezultat = await patchNalog(nalogId, { status: 'ZAVRSENO' });
- setLoading(false);
-
- if (rezultat) {
- setStatus('ZAVRSENO');
- window.showToast?.("Status ažuriran", "success");
- }
- };
-
- return (
- Učitavanje strojeva...
;
+import UniversalList from '../common/UniversalList.jsx';
+import { strojevi, setStrojevi } from '../../stores/fleetStore.js';
+import { getStrojevi } from '../../lib/api.js';
+export default function FleetStrojeviLista({ limit = 10 }) {
return (
-
- {list.map(s => (
+
+ {(s, baseUrl) => (
{s.naziv}
@@ -33,13 +31,13 @@ export default function FleetStrojeviLista({ initialData = [] }) {
Pregled detalja
- ))}
-
+ )}
+
);
}
\ No newline at end of file
diff --git a/002.FRONTEND/src/components/FleetVozilaLista.jsx b/002.FRONTEND/src/components/lista/FleetVozilaLista.jsx
similarity index 62%
rename from 002.FRONTEND/src/components/FleetVozilaLista.jsx
rename to 002.FRONTEND/src/components/lista/FleetVozilaLista.jsx
index 5b340c8..28858cf 100644
--- a/002.FRONTEND/src/components/FleetVozilaLista.jsx
+++ b/002.FRONTEND/src/components/lista/FleetVozilaLista.jsx
@@ -1,21 +1,20 @@
-// src/components/fleet/FleetVozilaLista.jsx
+// src/components/lista/FleetVozilaLista.jsx
import { h } from 'preact';
-import { useEffect } from 'preact/hooks';
-import { useStore } from '@nanostores/preact';
-import { vozila, setVozila } from '../stores/fleetStore';
-
-export default function FleetVozilaLista({ initialData = [], baseUrl }) {
- useEffect(() => {
- if (initialData.length > 0) setVozila(initialData);
- }, [initialData]);
-
- const list = useStore(vozila);
-
- if (!list.length) return
Učitavanje voznog parka...
;
+import UniversalList from '../common/UniversalList.jsx';
+import { vozila, setVozila } from '../../stores/fleetStore.js';
+import { getVozila } from '../../lib/api.js';
+export default function FleetVozilaLista({ limit = 10 }) {
return (
-
- {list.map(v => (
+
+ {/* Ovdje primaš SAMO JEDAN objekt (v) i bazni URL */}
+ {(v, baseUrl) => (
{v.naziv}
@@ -23,12 +22,12 @@ export default function FleetVozilaLista({ initialData = [], baseUrl }) {
{v.status_prikaz}
-
+
Registracija: {v.registracija}
Trenutna km: {v.trenutni_kilometri.toLocaleString()} km
-
+
- ))}
-
+ )}
+
);
}
\ No newline at end of file
diff --git a/002.FRONTEND/src/components/lista/RadniNalogLista.jsx b/002.FRONTEND/src/components/lista/RadniNalogLista.jsx
new file mode 100644
index 0000000..040d32a
--- /dev/null
+++ b/002.FRONTEND/src/components/lista/RadniNalogLista.jsx
@@ -0,0 +1,28 @@
+// src/components/lista/RadniNalogLista.jsx
+import { h } from 'preact';
+import UniversalList from '../common/UniversalList.jsx';
+// Pretpostavljam da koristiš store za nalozi, ne za strojeve
+import { radniNalozi, setRadniNalozi } from '../../stores/operativaStore.js';
+import { getRadniNalozi } from '../../lib/api.js';
+
+export default function RadniNalogLista({ limit = 10 }) {
+ return (
+
+ {(n, baseUrl) => (
+ // Ovdje iscrtavaš SAMO JEDAN element (li ili div)
+
+ {n.broj_naloga} -
+
+ Pregled
+
+
+ )}
+
+ );
+}
\ No newline at end of file
diff --git a/002.FRONTEND/src/components/novi/NoviRadniNalogForm.jsx b/002.FRONTEND/src/components/novi/NoviRadniNalogForm.jsx
index 695a156..e5d6dbc 100644
--- a/002.FRONTEND/src/components/novi/NoviRadniNalogForm.jsx
+++ b/002.FRONTEND/src/components/novi/NoviRadniNalogForm.jsx
@@ -2,157 +2,108 @@
import { h } from "preact";
import { useState, useEffect } from "preact/hooks";
import { navigate } from "astro:transitions/client";
-import { createNalog, getPutniNalozi, getStrojeviData, fetchKupciData } from "../../lib/api";
+import { createNalog, getPutniNalozi, getStrojevi, getKupci } from "../../lib/api";
import { showToast } from "../../stores/toastStore";
import Button from "../Button.jsx";
+// DRY komponenta za selekciju polja
+const FormSelect = ({ label, items, onChange, placeholder, disabled, renderItem }) => (
+
+ {label}
+
+ -- {placeholder} --
+ {items.map(item => renderItem(item))}
+
+
+);
+
export default function NoviRadniNalogForm() {
- const [putniNalogMode, setPutniNalogMode] = useState('none');
- const [putniNalozi, setPutniNalozi] = useState([]);
- const [selectedPutniId, setSelectedPutniId] = useState('');
+ const [formData, setFormData] = useState({ klijent: '', stroj: '', opis_kvara: '', putni_mode: 'none', putni_id: '' });
+ const [data, setData] = useState({ klijenti: [], strojevi: [], putniNalozi: [] });
+ const [loading, setLoading] = useState(false);
+
+ // 1. Inicijalni dohvat klijenata
+ useEffect(() => {
+ getKupci().then(c => setData(prev => ({ ...prev, klijenti: c })));
+ }, []);
+
+ // 2. Dohvat strojeva ovisno o odabranom klijentu
+ useEffect(() => {
+ if (formData.klijent) {
+ getStrojevi(formData.klijent).then(s => setData(prev => ({ ...prev, strojevi: s })));
+ } else {
+ setData(prev => ({ ...prev, strojevi: [] }));
+ }
+ }, [formData.klijent]);
+
+ // 3. Dohvat putnih naloga samo ako je odabran mod "existing"
+ useEffect(() => {
+ if (formData.putni_mode === 'existing') {
+ getPutniNalozi().then(pn => setData(prev => ({ ...prev, putniNalozi: pn })));
+ }
+ }, [formData.putni_mode]);
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ setLoading(true);
- const [klijenti, setKlijenti] = useState([]);
- const [klijentId, setKlijentId] = useState('');
- const [strojevi, setStrojevi] = useState([]);
- const [selectedStrojId, setSelectedStrojId] = useState('');
+ const payload = new FormData(e.target);
+ if (formData.putni_mode === 'new') payload.append('kreiraj_putni', 'true');
+ // Ako je existing, FormData već ima 'putni_nalog' polje iz select-a
- const [isFetchingNaloge, setIsFetchingNaloge] = useState(false);
- const [isFetchingStrojevi, setIsFetchingStrojevi] = useState(false);
- const [loading, setLoading] = useState(false);
+ const success = await createNalog(payload);
+ if (success) {
+ showToast("Radni nalog uspješno kreiran!", "success");
+ navigate('/operativa/radni-nalozi');
+ } else {
+ setLoading(false);
+ }
+ };
- // Dohvati listu klijenata pri učitavanju forme
- useEffect(() => {
- // Pretpostavljam da imaš funkciju getKlijenti() u api.js
- fetchKupciData().then(data => setKlijenti(data));
- }, []);
+ return (
+
+ );
}
\ No newline at end of file
diff --git a/002.FRONTEND/src/layouts/Layout.astro b/002.FRONTEND/src/layouts/Layout.astro
index 6e31c38..bdb1679 100644
--- a/002.FRONTEND/src/layouts/Layout.astro
+++ b/002.FRONTEND/src/layouts/Layout.astro
@@ -72,13 +72,13 @@ const { title } = Astro.props;
))}
-
+
-
+
diff --git a/002.FRONTEND/src/lib/nav.js b/002.FRONTEND/src/lib/nav.js
new file mode 100644
index 0000000..428923c
--- /dev/null
+++ b/002.FRONTEND/src/lib/nav.js
@@ -0,0 +1,7 @@
+// src/lib/nav.js
+import siteConfig from '../data/site.json';
+
+export function getBaseUrl(name) {
+ const item = siteConfig.navigation.find(n => n.name === name);
+ return item ? item.url : '/';
+}
\ No newline at end of file
diff --git a/002.FRONTEND/src/pages/fleet/strojevi.astro b/002.FRONTEND/src/pages/fleet/strojevi.astro
index f31d1c1..421b5f6 100644
--- a/002.FRONTEND/src/pages/fleet/strojevi.astro
+++ b/002.FRONTEND/src/pages/fleet/strojevi.astro
@@ -1,6 +1,7 @@
---
+// srs/pages/fleet/strojevi.astro
import Layout from '../../layouts/Layout.astro';
-import FleetStrojeviLista from '../../components/FleetStrojeviLista.jsx';
+import FleetStrojeviLista from '../../components/lista/FleetStrojeviLista.jsx';
import { getStrojevi } from '../../lib/api'; // Ovdje koristi getStrojevi
const strojeviData = await getStrojevi(); // I ovdje koristi getStrojevi
@@ -10,6 +11,6 @@ const strojeviData = await getStrojevi(); // I ovdje koristi getStrojevi
Pregled strojeva
-
+
\ No newline at end of file
diff --git a/002.FRONTEND/src/pages/index.astro b/002.FRONTEND/src/pages/index.astro
index 97af0ec..f12c17c 100644
--- a/002.FRONTEND/src/pages/index.astro
+++ b/002.FRONTEND/src/pages/index.astro
@@ -2,11 +2,9 @@
// src/pages/index.astro
import Layout from "../layouts/Layout.astro";
import siteConfig from "../data/site.json";
-import { getRadniNalozi, getVozila } from "../lib/api";
-import RadniNalogLista from '../components/RadniNalogLista.jsx';
-
-const nalozi = await getRadniNalozi();
-const vozila = await getVozila();
+import RadniNalogLista from '../components/lista/RadniNalogLista.jsx';
+import FleetStrojeviLista from '../components/lista/FleetStrojeviLista.jsx';
+import FleetVozilaLista from "../components/lista/FleetVozilaLista";
---
@@ -18,19 +16,18 @@ const vozila = await getVozila();
Radni nalozi
-
+ {/* client:only osigurava da se lista sama dohvati u pregledniku */}
+
Stanje flote
-
-
Ukupno vozila: {vozila.length}
-
- {vozila.map((vozilo) => (
- {vozilo.marka} {vozilo.model} ({vozilo.registracija})
- ))}
-
-
+
+
+
+
diff --git a/biljeske/SKELETON_LIST_VIEW.md b/biljeske/SKELETON_LIST_VIEW.md
new file mode 100644
index 0000000..1fc3f50
--- /dev/null
+++ b/biljeske/SKELETON_LIST_VIEW.md
@@ -0,0 +1,114 @@
+## Skeleton za List View
+
+```jsx
+// src/components/common/UniversalList.jsx
+import { h } from 'preact';
+import { useEffect, useState } from 'preact/hooks';
+import { useStore } from '@nanostores/preact';
+import { getBaseUrl } from '../lib/nav';
+import LoadingSpinner from './LoadingSpinner.jsx';
+
+export default function UniversalList({ store, setter, fetchMethod, limit, baseName, children }) {
+ const [loading, setLoading] = useState(true);
+ const list = useStore(store);
+
+ // Ako je baseName poslan, dohvati URL, inače ostavi undefined
+ const baseUrl = baseName ? getBaseUrl(baseName) : undefined;
+
+ useEffect(() => {
+ async function fetchData() {
+ if (list.length > 0) {
+ setLoading(false);
+ return;
+ }
+
+ try {
+ setLoading(true);
+ const data = await fetchMethod();
+ setter(data || []);
+ } catch (error) {
+ console.error("Greška pri dohvatu:", error);
+ setter([]);
+ } finally {
+ setTimeout(() => setLoading(false), 1000);
+ }
+ }
+ fetchData();
+ }, []);
+
+ if (loading) return ;
+
+ const displayList = limit ? list.slice(0, limit) : list;
+
+ return (
+
+ {/* children funkcija sada prima item i opcionalni baseUrl */}
+ {displayList.map(item => children(item, baseUrl))}
+
+ );
+}
+```
+
+## Loading komponenta
+
+```jsx
+// src/components/common/LoadingSpinner.jsx
+import { h } from 'preact';
+
+export default function LoadingSpinner({ text = "Učitavanje podataka..." }) {
+ return (
+
+ );
+}
+```
+
+## Komponenta FleetStrojeviLista.jsx koristi Skeleton
+
+```jsx
+/// src/components/fleet/FleetStrojeviLista.jsx
+import { h } from 'preact';
+import UniversalList from '../common/UniversalList.jsx';
+import { strojevi, setStrojevi } from '../stores/fleetStore';
+import { getStrojevi } from '../lib/api';
+
+export default function FleetStrojeviLista({ limit = 10 }) {
+ return (
+
+ {(s, baseUrl) => (
+
+
+
{s.naziv}
+
+ {s.tip_human_readable}
+
+
+
+
+ Marka/Model: {s.marka} {s.model_stroja}
+ Serijski br: {s.serijski_broj}
+ Vlasnik: {s.vlasnik_naziv}
+ Radni sati: {s.radni_sati} h
+ {s.registracija && Reg: {s.registracija} }
+
+
+
+ Pregled detalja
+
+
+ )}
+
+ );
+}
+```
\ No newline at end of file