diff --git a/.gitignore b/.gitignore index 427aec6..52ca13f 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,7 @@ dist/ downloads/ eggs/ .eggs/ -lib/ +#lib/ lib64/ parts/ sdist/ diff --git a/001.BACKEND/fleet/management/commands/populate_stroj.py b/001.BACKEND/fleet/management/commands/populate_stroj.py index 0f39b43..ff18900 100644 --- a/001.BACKEND/fleet/management/commands/populate_stroj.py +++ b/001.BACKEND/fleet/management/commands/populate_stroj.py @@ -6,7 +6,7 @@ from fleet.models import Stroj from kupci.models import Kupac class Command(BaseCommand): - help = 'Popunjava bazu podataka isključivo Liebherr strojevima koristeći Faker' + help = 'Popunjava bazu podataka isključivo Liebherr strojevima sinkronizirano s logistikom flote' def add_arguments(self, parser): parser.add_argument( @@ -18,69 +18,86 @@ class Command(BaseCommand): def handle(self, *args, **kwargs): total = kwargs['total'] - fake = Faker(['hr_HR']) # Koristimo hrvatski lokalitet + fake = Faker(['hr_HR']) kupci = list(Kupac.objects.all()) if not kupci: self.stdout.write(self.style.ERROR("Greška: Nema kupaca u bazi. Prvo pokreni populate_kupci!")) return - # Katalog usklađen s tvojim TIP_STROJA choices + # Bogatiji katalog usklađen s tvorničkim Liebherr oznakama i tvojim TIP_STROJA choices liebherr_katalog = { 'dizalica_toranj': [ '132 EC-H 8 Litronic', '280 EC-H 12', '81 K.1', - '172 EC-B 8' + '172 EC-B 8', + '125 K' ], 'dizalica_auto': [ 'LTM 1030-2.1', 'LTM 1050-3.1', 'LTM 1060-3.1', 'LTM 1120-4.1', + 'LTM 1350-6.1', # Dodan LTM 1350 (sukladno planiranim modifikacijama ventila) 'LTC 1050-3.1' ] } - self.stdout.write(self.style.MIGRATE_HEADING(f'🚀 Generiram {total} Liebherr strojeva...')) + self.stdout.write(self.style.MIGRATE_HEADING(f'🚀 Generiram {total} Liebherr strojeva u bazu...')) created_count = 0 for _ in range(total): - # Odabir tipa (usklađeno s tvojim modelom) tip = random.choice(['dizalica_toranj', 'dizalica_auto']) model_naziv = random.choice(liebherr_katalog[tip]) - # Faker za generiranje serijskog broja i registracije - serijski = f"LE-{fake.bothify(text='######')}" # npr. LE-482931 + # Odabiremo nasumičnog vlasnika za ovaj stroj + vlasnik_stroja = random.choice(kupci) - # Registracija samo za autodizalice (Faker format) + # Realistični Liebherr tvornički serijski brojevi (npr. LE-054321) + serijski = f"LE-{fake.numerify(text='0######')}" + + # USKLAĐENO: Registracija za autodizalice sada ima ispravne razmake i crticu + # Koristimo reg. oznaku koja odgovara sjedištu vlasnika ili nasumični HR grad reg = None if tip == 'dizalica_auto': - # Generira format sličan ZG-1234-LH - reg = f"ZG-{fake.numerify('####')}-LH" + gradovi = ['ZG', 'KA', 'RI', 'ST', 'OS', 'KR'] + grad = random.choice(gradovi) + reg_broj = fake.numerify(text='####') + reg_slova = fake.bothify(text='??', letters='ABCDEFGHIJKLMNOPQRSTUVWXYZ') + reg = f"{grad} {reg_broj}-{reg_slova}" - # Radni sati - sati = random.uniform(500.0, 12000.0) if tip == 'dizalica_toranj' else random.uniform(100.0, 5000.0) + # Radni sati dizalice + sati = random.uniform(800.0, 15000.0) if tip == 'dizalica_toranj' else random.uniform(200.0, 6000.0) - # Faker za datume (unutar zadnjih godinu dana) + # Atesti unutar zadnjih godinu dana datum_atesta = fake.date_between(start_date='-1y', end_date='today') + # USKLAĐENO: Generiramo lokaciju. Ako klijent/vlasnik ima definiran grad, + # stroj se primarno nalazi na gradilištu u tom gradu (ili okolici), inače koristimo fake.city() + lokacija_stroja = vlasnik_stroja.grad if (vlasnik_stroja and hasattr(vlasnik_stroja, 'grad')) else fake.city() + + # Nasumična simulacija aktivnih gradilišta u regiji + if random.random() > 0.6: + lokacija_stroja = f"Gradilište {lokacija_stroja}" + try: Stroj.objects.create( - vlasnik=random.choice(kupci), + vlasnik=vlasnik_stroja, naziv=f"Liebherr {model_naziv}", serijski_broj=serijski, marka='Liebherr', model_stroja=model_naziv, - godina_proizvodnje=random.randint(2015, 2024), + godina_proizvodnje=random.randint(2012, 2025), tip=tip, - radni_sati=round(sati, 2), + radni_sati=round(sati, 1), # Zaokružujemo na 1 decimalu sukladno formama registracija=reg, datum_zadnjeg_atesta=datum_atesta ) created_count += 1 + self.stdout.write(f" ✅ Kreiran: Liebherr {model_naziv} (Vlasnik: {vlasnik_stroja.naziv})") except Exception as e: - self.stderr.write(f"Greška kod S/N {serijski}: {e}") + self.stderr.write(f" ❌ Greška kod S/N {serijski}: {e}") continue - self.stdout.write(self.style.SUCCESS(f'✅ Uspješno dodano {created_count} Liebherr strojeva!')) \ No newline at end of file + self.stdout.write(self.style.SUCCESS(f'\nGotovo! Uspješno dodano {created_count} Liebherr strojeva u bazu.')) \ No newline at end of file diff --git a/001.BACKEND/fleet/management/commands/populate_vozila.py b/001.BACKEND/fleet/management/commands/populate_vozila.py index 4c7e4cc..92c8c51 100644 --- a/001.BACKEND/fleet/management/commands/populate_vozila.py +++ b/001.BACKEND/fleet/management/commands/populate_vozila.py @@ -1,11 +1,12 @@ import random from django.core.management.base import BaseCommand from django.core.exceptions import ValidationError +from django.db import IntegrityError from faker import Faker from fleet.models import Vozilo class Command(BaseCommand): - help = 'Popunjava bazu podataka s gospodarskim vozilima koristeći Faker' + help = 'Popunjava bazu podataka s gospodarskim vozilima sinkronizirano s Astro logistikom' def add_arguments(self, parser): parser.add_argument( @@ -19,7 +20,7 @@ class Command(BaseCommand): total = kwargs['total'] fake = Faker(['hr_HR']) - # Modeli koji se pojavljuju u tvom radnom nalogu (CSV) + # Modeli koji odgovaraju tvom voznom parku za mobilne servisne timove modeli_podaci = [ 'VW Transporter T6', 'Mercedes-Benz Vito', @@ -30,42 +31,56 @@ class Command(BaseCommand): 'Opel Vivaro', ] - self.stdout.write(self.style.MIGRATE_HEADING(f'🚀 Kreiram {total} vozila u flotu...')) + self.stdout.write(self.style.MIGRATE_HEADING(f'🚀 Kreiram {total} servisnih vozila u flotu...')) brojac = 0 - while brojac < total: + maksimalni_pokusaji = total * 5 + pokusaji = 0 + + while brojac < total and pokusaji < maksimalni_pokusaji: + pokusaji += 1 model_naziv = random.choice(modeli_podaci) - # Generiranje registracije koristeći Faker numerify/bothify - # Format: ZG 1234 AB - gradovi = ['ZG', 'ST', 'RI', 'OS', 'VZ', 'PU', 'KA', 'KC'] + # Generiranje realistične registracije u formatu: ZG 1234-AB + gradovi = ['ZG', 'KR', 'KA', 'ST', 'RI', 'OS', 'VZ', 'PU'] grad = random.choice(gradovi) - reg_oznaka = fake.numerify(text='####') # npr. 6732 - reg_slova = fake.bothify(text='??', letters='ABCDEFGHIJKLMNOPQRSTUVWXYZ') # npr. II - registracija = f"{grad}{reg_oznaka}{reg_slova}" - # Kilometri: Koristimo tvoju logiku da trenutni budu veći od početnih - pocetni = random.randint(0, 150000) - dodatni = random.randint(500, 20000) + reg_broj = fake.numerify(text=random.choice(['###', '####'])) + reg_slova = fake.bothify(text=random.choice(['?', '??']), letters='ABCDEFGHIJKLMNOPQRSTUVWXYZ') + registracija = f"{grad} {reg_broj}-{reg_slova}" + + # Kilometraža: Trenutni kilometri moraju pratiti povijest korištenja vozila + pocetni = random.randint(10000, 220000) + dodatni = random.randint(1500, 45000) trenutni = pocetni + dodatni + # POPRAVLJENO: Koristimo mala slova sukladno tvojim Choice ograničenjima u modelu + status_izbor = random.choice(['aktivan', 'aktivan', 'servis', 'neaktivan']) + try: - # Koristimo registraciju u nazivu radi lakšeg prepoznavanja u Astro frontendu + # Provjeravamo postoji li već ova registracija u bazi unaprijed + if Vozilo.objects.filter(registracija=registracija).exists(): + continue + vozilo = Vozilo.objects.create( - naziv=f"{model_naziv} ({grad})", + naziv=model_naziv, registracija=registracija, pocetni_kilometri=pocetni, trenutni_kilometri=trenutni, - status=random.choice(['aktivan', 'aktivan', 'servis', 'neaktivan']) + status=status_izbor ) brojac += 1 - self.stdout.write(f" ✅ Kreirano: {vozilo.naziv} [{vozilo.registracija}]") + self.stdout.write(f" ✅ Kreirano: {vozilo.naziv} [{vozilo.registracija}] | Status: {status_izbor} ({brojac}/{total})") - except ValidationError: - # Ako Faker pogodi postojeću registraciju, samo nastavi dalje + except IntegrityError: + # Ako baza ipak prijavi duplikat registracije unutar transakcije, samo idemo dalje + continue + except ValidationError as ve: + # Ako se pojavi neka druga validacijska greška, ispiši je da znamo o čemu se radi + self.stderr.write(f" ⚠️ Validacijska greška za {model_naziv}: {ve}") continue except Exception as e: - self.stderr.write(f" ❌ Greška: {e}") + self.stderr.write(f" ❌ Kritična greška pri upisu vozila: {e}") break - self.stdout.write(self.style.SUCCESS(f'\nGotovo! Uspješno dodano {brojac} vozila u flotu.')) \ No newline at end of file + self.stdout.write(self.style.SUCCESS(f'\nGotovo! Uspješno dodano {brojac} servisnih vozila u bazu flote.')) \ No newline at end of file diff --git a/001.BACKEND/fleet/migrations/0001_initial.py b/001.BACKEND/fleet/migrations/0001_initial.py index 27f36d9..5cd2b4c 100644 --- a/001.BACKEND/fleet/migrations/0001_initial.py +++ b/001.BACKEND/fleet/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.5 on 2026-05-20 20:03 +# Generated by Django 6.0.5 on 2026-05-22 03:53 import django.db.models.deletion from django.db import migrations, models diff --git a/001.BACKEND/kalendar/migrations/0001_initial.py b/001.BACKEND/kalendar/migrations/0001_initial.py index f257735..019583c 100644 --- a/001.BACKEND/kalendar/migrations/0001_initial.py +++ b/001.BACKEND/kalendar/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.5 on 2026-05-20 20:04 +# Generated by Django 6.0.5 on 2026-05-22 03:53 import django.db.models.deletion from django.conf import settings diff --git a/001.BACKEND/kalendar/serializers.py b/001.BACKEND/kalendar/serializers.py index a20f25c..0de53f6 100644 --- a/001.BACKEND/kalendar/serializers.py +++ b/001.BACKEND/kalendar/serializers.py @@ -1,9 +1,9 @@ from rest_framework import serializers from .models import Dogadaj -from operations.serializers import RadniNalogListaSerializer +import re class DogadajSerializer(serializers.ModelSerializer): - # 1. Mapiranje za standardne kalendarske knjižnice (FullCalendar traži title, start, end) + # 1. Mapiranje za standardne kalendarske knjižnice title = serializers.CharField(source='naslov') start = serializers.DateTimeField(source='pocetak') end = serializers.DateTimeField(source='kraj') @@ -16,23 +16,27 @@ class DogadajSerializer(serializers.ModelSerializer): boja = serializers.SerializerMethodField() je_radni_nalog = serializers.SerializerMethodField() + # 4. NOVA RAZDVOJENA POLJA ZA VOCILO I ČISTI OPIS KWARTA + vozilo_naziv = serializers.SerializerMethodField() + opis_cisti = serializers.SerializerMethodField() + class Meta: model = Dogadaj fields = [ 'id', 'title', 'start', 'end', 'tip', 'tip_display', - 'opis', 'izvrsitelj_ime', 'boja', 'je_radni_nalog', - 'klijent', 'vozilo', 'radni_nalog' + 'opis', 'opis_cisti', 'vozilo_naziv', 'izvrsitelj_ime', + 'boja', 'je_radni_nalog', 'klijent', 'vozilo', 'radni_nalog' ] def get_izvrsitelj_ime(self, obj): """Dohvaća ime preko tvog @property izvrsitelj u modelu""" - korisnik = obj.izvrsitelj # Poziva @property iz models.py + korisnik = obj.izvrsitelj if korisnik: return korisnik.get_full_name() or korisnik.username return "Nije dodijeljeno" def get_boja(self, obj): - """Dodjeljuje boju ovisno o tipu događaja za vizualni kalendar""" + """Dodjeljuje boju ovisno o tipu događaja""" boje = { 'servis': '#3498db', # Plava 'isporuka': '#2ecc71', # Zelena @@ -43,4 +47,31 @@ class DogadajSerializer(serializers.ModelSerializer): def get_je_radni_nalog(self, obj): """Pomaže frontendu da zna treba li prikazati link na radni nalog""" - return obj.radni_nalog is not None \ No newline at end of file + return obj.radni_nalog is not None + + def _parsiraj_spojeni_opis(self, obj): + """Pomoćna metoda koja analizira tekst i vraća (vozilo, opis)""" + sirovi_tekst = obj.opis or "" + + # Ako tekst sadrži ključne riječi, razdvajamo ga pomoću Regexa na backendu + if "Vozilo:" in sirovi_tekst or "Opis kvara:" in sirovi_tekst: + vozilo_match = re.search(r"Vozilo:\s*(.*?)(?=\s*Opis kvara:|$)", sirovi_tekst, re.IGNORECASE) + opis_match = re.search(r"Opis kvara:\s*(.*)", sirovi_tekst, re.IGNORECASE) + + vozilo = vozilo_match.group(1).strip() if vozilo_match else None + opis = opis_match.group(1).strip() if opis_match else sirovi_tekst + return vozilo, opis + + # Ako je polje vozilo na modelu već popunjeno kao relacija/tekst, iskoristi ga + model_vozilo = str(obj.vozilo) if obj.vozilo else None + return model_vozilo, sirovi_tekst + + def get_vozilo_naziv(self, obj): + """Vraća samo čisti naziv vozila""" + vozilo, _ = self._parsiraj_spojeni_opis(obj) + return vozilo + + def get_opis_cisti(self, obj): + """Vraća samo čisti tekst opisa kvara""" + _, opis = self._parsiraj_spojeni_opis(obj) + return opis if opis else "Nema dodatnog opisa." \ No newline at end of file diff --git a/001.BACKEND/kupci/management/commands/populate_kupci.py b/001.BACKEND/kupci/management/commands/populate_kupci.py index 1dedc38..5d6fb67 100644 --- a/001.BACKEND/kupci/management/commands/populate_kupci.py +++ b/001.BACKEND/kupci/management/commands/populate_kupci.py @@ -4,7 +4,7 @@ from faker import Faker from kupci.models import Kupac class Command(BaseCommand): - help = 'Popunjava bazu podataka s testnim kupcima' + help = 'Popunjava bazu podataka s testnim kupcima spremnim za logistički modul' def add_arguments(self, parser): parser.add_argument( @@ -18,7 +18,21 @@ class Command(BaseCommand): total = kwargs['total'] fake = Faker(['hr_HR']) - self.stdout.write(self.style.MIGRATE_HEADING(f'🚀 Kreiram {total} kupaca...')) + self.stdout.write(self.style.MIGRATE_HEADING(f'🚀 Kreiram {total} testnih kupaca...')) + + # Kontrolirani skup gradova u nominativu za savršen ispis relacija i odredišta u logistici + hr_gradovi = [ + {'grad': 'Zagreb', 'pbr': '10000'}, + {'grad': 'Karlovac', 'pbr': '47000'}, + {'grad': 'Ozalj', 'pbr': '47280'}, + {'grad': 'Split', 'pbr': '21000'}, + {'grad': 'Rijeka', 'pbr': '51000'}, + {'grad': 'Osijek', 'pbr': '31000'}, + {'grad': 'Varaždin', 'pbr': '42000'}, + {'grad': 'Zadar', 'pbr': '23000'}, + {'grad': 'Velika Gorica', 'pbr': '10410'}, + {'grad': 'Sisak', 'pbr': '44000'} + ] created_count = 0 for _ in range(total): @@ -29,27 +43,33 @@ class Command(BaseCommand): else: naziv = fake.name() - # Generiramo OIB (nasumičnih 11 znamenki) + # Generiramo OIB (točno 11 znamenki u string formatu) oib = "".join([str(random.randint(0, 9)) for _ in range(11)]) + + # Odabiremo grad i poštanski broj u paru kako bi podaci imali smisla + lokacija = random.choice(hr_gradovi) + + # Generiramo telefonski broj koji je čist i siguran za tel: linkove u Astru + pozivni = random.randint(1, 9) + telefon = f"+385 {pozivni} {random.randint(100, 999)}-{random.randint(1000, 9999)}" try: - # Koristimo get_or_create ili create da izbjegnemo duplikate OIB-a - # ako je polje unique=True u modelu + # Upotrebljavamo create, a jedinstvenost OIB-a hvatamo u iznimci Kupac.objects.create( naziv=naziv, oib=oib, email=fake.email(), - telefon=fake.phone_number(), + telefon=telefon, adresa=fake.street_address(), - grad=fake.city(), - postanski_broj=fake.postcode(), + grad=lokacija['grad'], # Usklađeno: čisti nominativ (npr. 'Karlovac') + postanski_broj=lokacija['pbr'], # Usklađeno: odgovarajući poštanski broj tip=tip, - napomena="Automatski generiran testni kupac.", + napomena="Automatski generiran testni kupac usklađen s modulom operativne logistike.", aktivno=True ) created_count += 1 except Exception as e: - # Ako OIB slučajno bude isti, samo nastavi dalje + # Ako OIB slučajno kolidira (unutar unique=True), preskoči i nastavi generirati sljedećeg continue - self.stdout.write(self.style.SUCCESS(f'✅ Uspješno dodano {created_count} novih kupaca!')) \ No newline at end of file + self.stdout.write(self.style.SUCCESS(f'✅ Uspješno dodano {created_count} novih kupaca u bazu!')) \ No newline at end of file diff --git a/001.BACKEND/kupci/migrations/0001_initial.py b/001.BACKEND/kupci/migrations/0001_initial.py index 4f00d33..12a1327 100644 --- a/001.BACKEND/kupci/migrations/0001_initial.py +++ b/001.BACKEND/kupci/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.5 on 2026-05-20 20:03 +# Generated by Django 6.0.5 on 2026-05-22 03:52 from django.db import migrations, models diff --git a/001.BACKEND/media/radni-nalozi/2026-0001/galerija/gal_2026-0001_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0001/galerija/gal_RN-2026-0001_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0001/galerija/gal_2026-0001_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0001/galerija/gal_RN-2026-0001_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0001/radni-nalozi/2026-0001/rn_2026-0001.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0001/radni-nalozi/RN-2026-0001/rn_RN-2026-0001.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0001/radni-nalozi/2026-0001/rn_2026-0001.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0001/radni-nalozi/RN-2026-0001/rn_RN-2026-0001.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0001/galerija/gal_2026-0001_1.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0001/rn_RN-2026-0001.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0001/galerija/gal_2026-0001_1.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0001/rn_RN-2026-0001.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0001/rn_2026-0001.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0002/galerija/gal_RN-2026-0002_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0001/rn_2026-0001.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0002/galerija/gal_RN-2026-0002_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0002/galerija/gal_2026-0002_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0002/galerija/gal_RN-2026-0002_1.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0002/galerija/gal_2026-0002_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0002/galerija/gal_RN-2026-0002_1.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0002/radni-nalozi/2026-0002/rn_2026-0002.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0002/radni-nalozi/RN-2026-0002/rn_RN-2026-0002.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0002/radni-nalozi/2026-0002/rn_2026-0002.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0002/radni-nalozi/RN-2026-0002/rn_RN-2026-0002.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0002/galerija/gal_2026-0002_1.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0002/rn_RN-2026-0002.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0002/galerija/gal_2026-0002_1.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0002/rn_RN-2026-0002.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0002/rn_2026-0002.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0003/galerija/gal_RN-2026-0003_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0002/rn_2026-0002.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0003/galerija/gal_RN-2026-0003_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0003/radni-nalozi/2026-0003/rn_2026-0003.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0003/radni-nalozi/RN-2026-0003/rn_RN-2026-0003.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0003/radni-nalozi/2026-0003/rn_2026-0003.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0003/radni-nalozi/RN-2026-0003/rn_RN-2026-0003.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0003/galerija/gal_2026-0003_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0003/rn_RN-2026-0003.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0003/galerija/gal_2026-0003_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0003/rn_RN-2026-0003.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0003/rn_2026-0003.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0004/galerija/gal_RN-2026-0004_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0003/rn_2026-0003.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0004/galerija/gal_RN-2026-0004_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0004/galerija/gal_2026-0004_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0004/galerija/gal_RN-2026-0004_1.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0004/galerija/gal_2026-0004_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0004/galerija/gal_RN-2026-0004_1.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0004/radni-nalozi/2026-0004/rn_2026-0004.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0004/radni-nalozi/RN-2026-0004/rn_RN-2026-0004.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0004/radni-nalozi/2026-0004/rn_2026-0004.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0004/radni-nalozi/RN-2026-0004/rn_RN-2026-0004.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0004/galerija/gal_2026-0004_1.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0004/rn_RN-2026-0004.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0004/galerija/gal_2026-0004_1.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0004/rn_RN-2026-0004.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0004/rn_2026-0004.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0005/galerija/gal_RN-2026-0005_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0004/rn_2026-0004.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0005/galerija/gal_RN-2026-0005_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0005/radni-nalozi/2026-0005/rn_2026-0005.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0005/radni-nalozi/RN-2026-0005/rn_RN-2026-0005.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0005/radni-nalozi/2026-0005/rn_2026-0005.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0005/radni-nalozi/RN-2026-0005/rn_RN-2026-0005.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0005/galerija/gal_2026-0005_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0005/rn_RN-2026-0005.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0005/galerija/gal_2026-0005_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0005/rn_RN-2026-0005.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0005/galerija/gal_2026-0005_1.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0006/galerija/gal_RN-2026-0006_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0005/galerija/gal_2026-0005_1.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0006/galerija/gal_RN-2026-0006_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0006/radni-nalozi/2026-0006/rn_2026-0006.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0006/radni-nalozi/RN-2026-0006/rn_RN-2026-0006.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0006/radni-nalozi/2026-0006/rn_2026-0006.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0006/radni-nalozi/RN-2026-0006/rn_RN-2026-0006.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0005/rn_2026-0005.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0006/rn_RN-2026-0006.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0005/rn_2026-0005.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0006/rn_RN-2026-0006.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0006/galerija/gal_2026-0006_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0007/galerija/gal_RN-2026-0007_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0006/galerija/gal_2026-0006_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0007/galerija/gal_RN-2026-0007_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0007/radni-nalozi/2026-0007/rn_2026-0007.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0007/radni-nalozi/RN-2026-0007/rn_RN-2026-0007.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0007/radni-nalozi/2026-0007/rn_2026-0007.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0007/radni-nalozi/RN-2026-0007/rn_RN-2026-0007.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0006/galerija/gal_2026-0006_1.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0007/rn_RN-2026-0007.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0006/galerija/gal_2026-0006_1.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0007/rn_RN-2026-0007.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0006/rn_2026-0006.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0008/galerija/gal_RN-2026-0008_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0006/rn_2026-0006.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0008/galerija/gal_RN-2026-0008_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0007/galerija/gal_2026-0007_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0008/galerija/gal_RN-2026-0008_1.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0007/galerija/gal_2026-0007_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0008/galerija/gal_RN-2026-0008_1.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0008/radni-nalozi/2026-0008/rn_2026-0008.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0008/radni-nalozi/RN-2026-0008/rn_RN-2026-0008.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0008/radni-nalozi/2026-0008/rn_2026-0008.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0008/radni-nalozi/RN-2026-0008/rn_RN-2026-0008.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0007/galerija/gal_2026-0007_1.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0008/rn_RN-2026-0008.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0007/galerija/gal_2026-0007_1.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0008/rn_RN-2026-0008.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0007/rn_2026-0007.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0009/galerija/gal_RN-2026-0009_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0007/rn_2026-0007.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0009/galerija/gal_RN-2026-0009_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0008/galerija/gal_2026-0008_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0009/galerija/gal_RN-2026-0009_1.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0008/galerija/gal_2026-0008_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0009/galerija/gal_RN-2026-0009_1.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0009/radni-nalozi/2026-0009/rn_2026-0009.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0009/radni-nalozi/RN-2026-0009/rn_RN-2026-0009.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0009/radni-nalozi/2026-0009/rn_2026-0009.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0009/radni-nalozi/RN-2026-0009/rn_RN-2026-0009.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0008/galerija/gal_2026-0008_1.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0009/rn_RN-2026-0009.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0008/galerija/gal_2026-0008_1.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0009/rn_RN-2026-0009.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0008/rn_2026-0008.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0010/galerija/gal_RN-2026-0010_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0008/rn_2026-0008.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0010/galerija/gal_RN-2026-0010_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0009/galerija/gal_2026-0009_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0010/galerija/gal_RN-2026-0010_1.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0009/galerija/gal_2026-0009_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0010/galerija/gal_RN-2026-0010_1.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0010/radni-nalozi/2026-0010/rn_2026-0010.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0010/radni-nalozi/RN-2026-0010/rn_RN-2026-0010.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0010/radni-nalozi/2026-0010/rn_2026-0010.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0010/radni-nalozi/RN-2026-0010/rn_RN-2026-0010.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0009/rn_2026-0009.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0010/rn_RN-2026-0010.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0009/rn_2026-0009.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0010/rn_RN-2026-0010.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0010/galerija/gal_2026-0010_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0011/galerija/gal_RN-2026-0011_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0010/galerija/gal_2026-0010_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0011/galerija/gal_RN-2026-0011_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0010/rn_2026-0010.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0011/galerija/gal_RN-2026-0011_1.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0010/rn_2026-0010.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0011/galerija/gal_RN-2026-0011_1.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0011/radni-nalozi/2026-0011/rn_2026-0011.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0011/radni-nalozi/RN-2026-0011/rn_RN-2026-0011.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0011/radni-nalozi/2026-0011/rn_2026-0011.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0011/radni-nalozi/RN-2026-0011/rn_RN-2026-0011.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0011/galerija/gal_2026-0011_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0011/rn_RN-2026-0011.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0011/galerija/gal_2026-0011_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0011/rn_RN-2026-0011.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0011/rn_2026-0011.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0012/galerija/gal_RN-2026-0012_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0011/rn_2026-0011.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0012/galerija/gal_RN-2026-0012_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0012/galerija/gal_2026-0012_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0012/galerija/gal_RN-2026-0012_1.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0012/galerija/gal_2026-0012_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0012/galerija/gal_RN-2026-0012_1.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0012/radni-nalozi/2026-0012/rn_2026-0012.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0012/radni-nalozi/RN-2026-0012/rn_RN-2026-0012.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0012/radni-nalozi/2026-0012/rn_2026-0012.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0012/radni-nalozi/RN-2026-0012/rn_RN-2026-0012.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0012/rn_2026-0012.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0012/rn_RN-2026-0012.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0012/rn_2026-0012.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0012/rn_RN-2026-0012.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0013/galerija/gal_2026-0013_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0013/galerija/gal_RN-2026-0013_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0013/galerija/gal_2026-0013_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0013/galerija/gal_RN-2026-0013_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0013/radni-nalozi/2026-0013/rn_2026-0013.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0013/radni-nalozi/RN-2026-0013/rn_RN-2026-0013.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0013/radni-nalozi/2026-0013/rn_2026-0013.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0013/radni-nalozi/RN-2026-0013/rn_RN-2026-0013.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0013/rn_2026-0013.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0013/rn_RN-2026-0013.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0013/rn_2026-0013.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0013/rn_RN-2026-0013.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0014/galerija/gal_2026-0014_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0014/galerija/gal_RN-2026-0014_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0014/galerija/gal_2026-0014_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0014/galerija/gal_RN-2026-0014_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0014/radni-nalozi/2026-0014/rn_2026-0014.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0014/radni-nalozi/RN-2026-0014/rn_RN-2026-0014.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0014/radni-nalozi/2026-0014/rn_2026-0014.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0014/radni-nalozi/RN-2026-0014/rn_RN-2026-0014.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0014/rn_2026-0014.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0014/rn_RN-2026-0014.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0014/rn_2026-0014.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0014/rn_RN-2026-0014.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0015/galerija/gal_2026-0015_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0015/galerija/gal_RN-2026-0015_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0015/galerija/gal_2026-0015_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0015/galerija/gal_RN-2026-0015_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0015/rn_2026-0015.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0015/galerija/gal_RN-2026-0015_1.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0015/rn_2026-0015.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0015/galerija/gal_RN-2026-0015_1.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0015/radni-nalozi/2026-0015/rn_2026-0015.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0015/radni-nalozi/RN-2026-0015/rn_RN-2026-0015.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0015/radni-nalozi/2026-0015/rn_2026-0015.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0015/radni-nalozi/RN-2026-0015/rn_RN-2026-0015.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0016/galerija/gal_2026-0016_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0015/rn_RN-2026-0015.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0016/galerija/gal_2026-0016_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0015/rn_RN-2026-0015.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0016/galerija/gal_2026-0016_1.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0016/galerija/gal_RN-2026-0016_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0016/galerija/gal_2026-0016_1.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0016/galerija/gal_RN-2026-0016_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0016/radni-nalozi/2026-0016/rn_2026-0016.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0016/radni-nalozi/RN-2026-0016/rn_RN-2026-0016.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0016/radni-nalozi/2026-0016/rn_2026-0016.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0016/radni-nalozi/RN-2026-0016/rn_RN-2026-0016.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0016/rn_2026-0016.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0016/rn_RN-2026-0016.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0016/rn_2026-0016.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0016/rn_RN-2026-0016.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0017/galerija/gal_2026-0017_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0017/galerija/gal_RN-2026-0017_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0017/galerija/gal_2026-0017_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0017/galerija/gal_RN-2026-0017_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0017/galerija/gal_2026-0017_1.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0017/galerija/gal_RN-2026-0017_1.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0017/galerija/gal_2026-0017_1.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0017/galerija/gal_RN-2026-0017_1.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0017/radni-nalozi/2026-0017/rn_2026-0017.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0017/radni-nalozi/RN-2026-0017/rn_RN-2026-0017.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0017/radni-nalozi/2026-0017/rn_2026-0017.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0017/radni-nalozi/RN-2026-0017/rn_RN-2026-0017.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0017/rn_2026-0017.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0017/rn_RN-2026-0017.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0017/rn_2026-0017.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0017/rn_RN-2026-0017.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0018/galerija/gal_2026-0018_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0018/galerija/gal_RN-2026-0018_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0018/galerija/gal_2026-0018_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0018/galerija/gal_RN-2026-0018_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0018/radni-nalozi/2026-0018/rn_2026-0018.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0018/radni-nalozi/RN-2026-0018/rn_RN-2026-0018.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0018/radni-nalozi/2026-0018/rn_2026-0018.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0018/radni-nalozi/RN-2026-0018/rn_RN-2026-0018.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0018/galerija/gal_2026-0018_1.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0018/rn_RN-2026-0018.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0018/galerija/gal_2026-0018_1.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0018/rn_RN-2026-0018.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0018/rn_2026-0018.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0019/galerija/gal_RN-2026-0019_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0018/rn_2026-0018.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0019/galerija/gal_RN-2026-0019_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0019/radni-nalozi/2026-0019/rn_2026-0019.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0019/radni-nalozi/RN-2026-0019/rn_RN-2026-0019.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0019/radni-nalozi/2026-0019/rn_2026-0019.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0019/radni-nalozi/RN-2026-0019/rn_RN-2026-0019.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0019/galerija/gal_2026-0019_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0019/rn_RN-2026-0019.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0019/galerija/gal_2026-0019_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0019/rn_RN-2026-0019.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0019/rn_2026-0019.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0020/galerija/gal_RN-2026-0020_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0019/rn_2026-0019.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0020/galerija/gal_RN-2026-0020_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0020/radni-nalozi/2026-0020/rn_2026-0020.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0020/radni-nalozi/RN-2026-0020/rn_RN-2026-0020.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0020/radni-nalozi/2026-0020/rn_2026-0020.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0020/radni-nalozi/RN-2026-0020/rn_RN-2026-0020.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0020/galerija/gal_2026-0020_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0020/rn_RN-2026-0020.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0020/galerija/gal_2026-0020_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0020/rn_RN-2026-0020.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0020/rn_2026-0020.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0021/galerija/gal_RN-2026-0021_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0020/rn_2026-0020.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0021/galerija/gal_RN-2026-0021_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0021/galerija/gal_2026-0021_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0021/galerija/gal_RN-2026-0021_1.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0021/galerija/gal_2026-0021_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0021/galerija/gal_RN-2026-0021_1.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0021/radni-nalozi/2026-0021/rn_2026-0021.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0021/radni-nalozi/RN-2026-0021/rn_RN-2026-0021.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0021/radni-nalozi/2026-0021/rn_2026-0021.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0021/radni-nalozi/RN-2026-0021/rn_RN-2026-0021.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0021/galerija/gal_2026-0021_1.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0021/rn_RN-2026-0021.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0021/galerija/gal_2026-0021_1.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0021/rn_RN-2026-0021.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0021/rn_2026-0021.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0022/galerija/gal_RN-2026-0022_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0021/rn_2026-0021.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0022/galerija/gal_RN-2026-0022_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0022/radni-nalozi/2026-0022/rn_2026-0022.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0022/radni-nalozi/RN-2026-0022/rn_RN-2026-0022.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0022/radni-nalozi/2026-0022/rn_2026-0022.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0022/radni-nalozi/RN-2026-0022/rn_RN-2026-0022.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0022/galerija/gal_2026-0022_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0022/rn_RN-2026-0022.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0022/galerija/gal_2026-0022_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0022/rn_RN-2026-0022.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0022/galerija/gal_2026-0022_1.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0023/galerija/gal_RN-2026-0023_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0022/galerija/gal_2026-0022_1.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0023/galerija/gal_RN-2026-0023_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0022/rn_2026-0022.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0023/galerija/gal_RN-2026-0023_1.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0022/rn_2026-0022.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0023/galerija/gal_RN-2026-0023_1.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0023/radni-nalozi/2026-0023/rn_2026-0023.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0023/radni-nalozi/RN-2026-0023/rn_RN-2026-0023.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0023/radni-nalozi/2026-0023/rn_2026-0023.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0023/radni-nalozi/RN-2026-0023/rn_RN-2026-0023.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0023/galerija/gal_2026-0023_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0023/rn_RN-2026-0023.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0023/galerija/gal_2026-0023_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0023/rn_RN-2026-0023.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0023/galerija/gal_2026-0023_1.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0024/galerija/gal_RN-2026-0024_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0023/galerija/gal_2026-0023_1.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0024/galerija/gal_RN-2026-0024_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0023/rn_2026-0023.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0024/galerija/gal_RN-2026-0024_1.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0023/rn_2026-0023.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0024/galerija/gal_RN-2026-0024_1.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0024/radni-nalozi/2026-0024/rn_2026-0024.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0024/radni-nalozi/RN-2026-0024/rn_RN-2026-0024.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0024/radni-nalozi/2026-0024/rn_2026-0024.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0024/radni-nalozi/RN-2026-0024/rn_RN-2026-0024.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0024/galerija/gal_2026-0024_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0024/rn_RN-2026-0024.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0024/galerija/gal_2026-0024_0.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0024/rn_RN-2026-0024.jpg diff --git a/001.BACKEND/media/radni-nalozi/2026-0024/galerija/gal_2026-0024_1.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0025/galerija/gal_RN-2026-0025_0.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0024/galerija/gal_2026-0024_1.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0025/galerija/gal_RN-2026-0025_0.jpg diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0025/radni-nalozi/RN-2026-0025/rn_RN-2026-0025.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0025/radni-nalozi/RN-2026-0025/rn_RN-2026-0025.jpg new file mode 100644 index 0000000..fc0e460 Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0025/radni-nalozi/RN-2026-0025/rn_RN-2026-0025.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/2026-0024/rn_2026-0024.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0025/rn_RN-2026-0025.jpg similarity index 100% rename from 001.BACKEND/media/radni-nalozi/2026-0024/rn_2026-0024.jpg rename to 001.BACKEND/media/radni-nalozi/RN-2026-0025/rn_RN-2026-0025.jpg diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0026/galerija/gal_RN-2026-0026_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0026/galerija/gal_RN-2026-0026_0.jpg new file mode 100644 index 0000000..7fe1d5c Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0026/galerija/gal_RN-2026-0026_0.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0026/radni-nalozi/RN-2026-0026/rn_RN-2026-0026.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0026/radni-nalozi/RN-2026-0026/rn_RN-2026-0026.jpg new file mode 100644 index 0000000..fc0e460 Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0026/radni-nalozi/RN-2026-0026/rn_RN-2026-0026.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0026/rn_RN-2026-0026.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0026/rn_RN-2026-0026.jpg new file mode 100644 index 0000000..7fe1d5c Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0026/rn_RN-2026-0026.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0027/galerija/gal_RN-2026-0027_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0027/galerija/gal_RN-2026-0027_0.jpg new file mode 100644 index 0000000..7fe1d5c Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0027/galerija/gal_RN-2026-0027_0.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0027/radni-nalozi/RN-2026-0027/rn_RN-2026-0027.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0027/radni-nalozi/RN-2026-0027/rn_RN-2026-0027.jpg new file mode 100644 index 0000000..fc0e460 Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0027/radni-nalozi/RN-2026-0027/rn_RN-2026-0027.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0027/rn_RN-2026-0027.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0027/rn_RN-2026-0027.jpg new file mode 100644 index 0000000..7fe1d5c Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0027/rn_RN-2026-0027.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0028/galerija/gal_RN-2026-0028_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0028/galerija/gal_RN-2026-0028_0.jpg new file mode 100644 index 0000000..7fe1d5c Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0028/galerija/gal_RN-2026-0028_0.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0028/galerija/gal_RN-2026-0028_1.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0028/galerija/gal_RN-2026-0028_1.jpg new file mode 100644 index 0000000..7fe1d5c Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0028/galerija/gal_RN-2026-0028_1.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0028/radni-nalozi/RN-2026-0028/rn_RN-2026-0028.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0028/radni-nalozi/RN-2026-0028/rn_RN-2026-0028.jpg new file mode 100644 index 0000000..fc0e460 Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0028/radni-nalozi/RN-2026-0028/rn_RN-2026-0028.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0028/rn_RN-2026-0028.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0028/rn_RN-2026-0028.jpg new file mode 100644 index 0000000..7fe1d5c Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0028/rn_RN-2026-0028.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0029/galerija/gal_RN-2026-0029_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0029/galerija/gal_RN-2026-0029_0.jpg new file mode 100644 index 0000000..7fe1d5c Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0029/galerija/gal_RN-2026-0029_0.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0029/galerija/gal_RN-2026-0029_1.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0029/galerija/gal_RN-2026-0029_1.jpg new file mode 100644 index 0000000..7fe1d5c Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0029/galerija/gal_RN-2026-0029_1.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0029/radni-nalozi/RN-2026-0029/rn_RN-2026-0029.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0029/radni-nalozi/RN-2026-0029/rn_RN-2026-0029.jpg new file mode 100644 index 0000000..fc0e460 Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0029/radni-nalozi/RN-2026-0029/rn_RN-2026-0029.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0029/rn_RN-2026-0029.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0029/rn_RN-2026-0029.jpg new file mode 100644 index 0000000..7fe1d5c Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0029/rn_RN-2026-0029.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0030/galerija/gal_RN-2026-0030_0.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0030/galerija/gal_RN-2026-0030_0.jpg new file mode 100644 index 0000000..7fe1d5c Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0030/galerija/gal_RN-2026-0030_0.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0030/galerija/gal_RN-2026-0030_1.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0030/galerija/gal_RN-2026-0030_1.jpg new file mode 100644 index 0000000..7fe1d5c Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0030/galerija/gal_RN-2026-0030_1.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0030/radni-nalozi/RN-2026-0030/rn_RN-2026-0030.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0030/radni-nalozi/RN-2026-0030/rn_RN-2026-0030.jpg new file mode 100644 index 0000000..fc0e460 Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0030/radni-nalozi/RN-2026-0030/rn_RN-2026-0030.jpg differ diff --git a/001.BACKEND/media/radni-nalozi/RN-2026-0030/rn_RN-2026-0030.jpg b/001.BACKEND/media/radni-nalozi/RN-2026-0030/rn_RN-2026-0030.jpg new file mode 100644 index 0000000..7fe1d5c Binary files /dev/null and b/001.BACKEND/media/radni-nalozi/RN-2026-0030/rn_RN-2026-0030.jpg differ diff --git a/001.BACKEND/operations/management/commands/populate_radninalog.py b/001.BACKEND/operations/management/commands/populate_radninalog.py index d7450f5..b42f098 100644 --- a/001.BACKEND/operations/management/commands/populate_radninalog.py +++ b/001.BACKEND/operations/management/commands/populate_radninalog.py @@ -4,7 +4,8 @@ from django.core.management.base import BaseCommand from django.contrib.auth import get_user_model from django.utils import timezone from django.core.files import File -from operations.models import RadniNalog, RadniNalogSlika, PutniNalog +from django.db import transaction +from operations.models import RadniNalog, RadniNalogSlika, PutniNalog, BrojacSekvence from fleet.models import Vozilo, Stroj from kupci.models import Kupac from kalendar.services import kreiraj_kalendarski_unos @@ -12,10 +13,10 @@ from kalendar.services import kreiraj_kalendarski_unos User = get_user_model() class Command(BaseCommand): - help = 'Popunjava bazu s Putnim nalozima, Radnim nalozima i slikama' + help = 'Popunjava bazu s Putnim nalozima, Radnim nalozima i slikama sinkronizirano sa sekvencama' def add_arguments(self, parser): - parser.add_argument('total_putovanja', type=int, help='Broj putnih naloga', default=3) + parser.add_argument('total_putovanja', type=int, nargs='?', default=3, help='Broj putnih naloga') parser.add_argument('--img', type=str, default='test_assets/kvar.jpg') def handle(self, *args, **kwargs): @@ -43,23 +44,45 @@ class Command(BaseCommand): "Hitna intervencija: Stroj u kvaru." ] - self.stdout.write(f'🚀 Generiranje {total_putovanja} putovanja...') + self.stdout.write(f'🚀 Generiranje {total_putovanja} putovanja sinkronizirano sa sekvencama...') success_count_rn = 0 trenutna_godina = timezone.now().year + # Definiramo ključ i prefikse za putne naloge + prefix_pn = f"PN-{trenutna_godina}-" + kljuc_sekvence_rn = f"radni_nalog_{trenutna_godina}" + for i in range(total_putovanja): serviser = random.choice(serviseri) vozilo = random.choice(vozila) - broj_pn = f"PN-{trenutna_godina}-{PutniNalog.objects.count() + 1:04d}" - # 1. Kreiraj PUTNI NALOG (u statusu OTVOREN) - putni_nalog = PutniNalog.objects.create( - broj_naloga=broj_pn, - vozilo=vozilo, - korisnik=serviser, - relacija="Zagreb - Teren - Zagreb", - mjesto_odredista="Teren", - ) + # 1. GENERIRANJE NEUNIŠTIVOG BROJA PUTNOG NALOGA + # Da skripta ne bi udarila u unique=True, tražimo stvarni zadnji PN u bazi + with transaction.atomic(): + zadnji_pn = PutniNalog.objects.select_for_update().filter( + broj_naloga__startswith=prefix_pn + ).order_by('-broj_naloga').first() + + if zadnji_pn: + try: + zadnji_broj_pn = int(zadnji_pn.broj_naloga.split('-')[-1]) + novi_broj_pn = zadnji_broj_pn + 1 + except (ValueError, IndexError): + novi_broj_pn = PutniNalog.objects.count() + 1 + else: + novi_broj_pn = 1 + + broj_pn = f"{prefix_pn}{novi_broj_pn:04d}" + + # Kreiraj PUTNI NALOG + putni_nalog = PutniNalog.objects.create( + broj_naloga=broj_pn, + vozilo=vozilo, + korisnik=serviser, + relacija="Zagreb - Teren - Zagreb", + mjesto_odredista="Teren", + vrijeme_polaska=timezone.now() + ) self.stdout.write(self.style.MIGRATE_LABEL(f"Otvoren {broj_pn} (Vozilo: {vozilo.registracija})")) @@ -69,14 +92,15 @@ class Command(BaseCommand): for j in range(za_odraditi): klijenti_sa_strojima = [k for k in kupci if k.strojevi.exists()] + if not klijenti_sa_strojima: + continue klijent = random.choice(klijenti_sa_strojima) stroj = random.choice(list(klijent.strojevi.all())) - broj_rn = f"{trenutna_godina}-{RadniNalog.objects.count() + 1:04d}" - try: + # Inicijaliziramo objekt bez broja_naloga kako bi se aktivirala + # naša neuništiva sekvencijska logika unutar save() metode modela! nalog = RadniNalog( - broj_naloga=broj_rn, klijent=klijent, stroj=stroj, putni_nalog=putni_nalog, @@ -85,11 +109,14 @@ class Command(BaseCommand): status=RadniNalog.StatusRadaChoices.PLANIRANO ) + # Prvo moramo izvršiti osnovni save da model kroz BrojacSekvence dodijeli čisti broj + nalog.save() + + # Ako slika postoji, dodajemo je NAKON što je broj kreiran (da putanja_slike_naloga ima ispravan folder) if test_img_path: with open(test_img_path, 'rb') as f: - nalog.slika_kvara.save(f'rn_{broj_rn}.jpg', File(f), save=False) + nalog.slika_kvara.save(f'rn_{nalog.broj_naloga}.jpg', File(f), save=True) - nalog.save() kreirani_radni_nalozi.append(nalog) # Dodatne slike u galeriju @@ -98,22 +125,24 @@ class Command(BaseCommand): with open(test_img_path, 'rb') as f: RadniNalogSlika.objects.create( radni_nalog=nalog, - slika=File(f, name=f'gal_{broj_rn}_{k}.jpg'), + slika=File(f, name=f'gal_{nalog.broj_naloga}_{k}.jpg'), opis=f"Detalj kvara {k+1}" ) # 5. Upis u kalendar - kreiraj_kalendarski_unos(nalog) + try: + kreiraj_kalendarski_unos(nalog) + except Exception as cal_err: + self.stdout.write(self.style.WARNING(f" ⚠️ Kalendar unos preskočen: {cal_err}")) success_count_rn += 1 - self.stdout.write(f" ✅ RN-{broj_rn} dodan u {broj_pn}") + self.stdout.write(f" ✅ {nalog.broj_naloga} uspješno povezan u {broj_pn}") except Exception as e: - self.stderr.write(f" ❌ Greška kod RN-{broj_rn}: {e}") + self.stderr.write(f" ❌ Greška kod kreiranja RN: {e}") # 3. NASUMIČNO ZATVARANJE (Simulacija završenog posla) - # 50% šanse da ćemo u skripti odmah "završiti" ovaj put - if random.random() > 0.5: + if random.random() > 0.5 and kreirani_radni_nalozi: try: # Prvo moramo zatvoriti sve radne naloge da bi PN prošao validaciju for rn in kreirani_radni_nalozi: diff --git a/001.BACKEND/operations/migrations/0001_initial.py b/001.BACKEND/operations/migrations/0001_initial.py index 1826186..e6a1427 100644 --- a/001.BACKEND/operations/migrations/0001_initial.py +++ b/001.BACKEND/operations/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.5 on 2026-05-20 20:03 +# Generated by Django 6.0.5 on 2026-05-22 03:53 import django.db.models.deletion import django.utils.timezone @@ -18,6 +18,18 @@ class Migration(migrations.Migration): ] operations = [ + migrations.CreateModel( + name='BrojacSekvence', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('naziv_sekvence', models.CharField(max_length=50, unique=True)), + ('zadnji_broj', models.PositiveIntegerField(default=0)), + ], + options={ + 'verbose_name': 'Brojač sekvence', + 'verbose_name_plural': 'Brojači sekvenci', + }, + ), migrations.CreateModel( name='PutniNalog', fields=[ @@ -45,7 +57,7 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('broj_naloga', models.CharField(blank=True, max_length=20, unique=True)), ('opis_kvara', models.TextField()), - ('status', models.CharField(choices=[('PLANIRANO', 'Planirano'), ('U_RADU', 'U radu'), ('ZAVRSENO', 'Završeno')], default='PLANIRANO', max_length=20)), + ('status', models.CharField(choices=[('PLANIRANO', 'Planirano'), ('U_RADU', 'U radu'), ('ZAVRSENO', 'Završen')], default='PLANIRANO', max_length=20)), ('datum_kreiranja', models.DateTimeField(auto_now_add=True, verbose_name='Datum kreiranja')), ('datum_azuriranja', models.DateTimeField(auto_now=True, verbose_name='Zadnja izmjena')), ('slika_kvara', models.ImageField(blank=True, null=True, upload_to=operations.models.putanja_slike_naloga)), diff --git a/001.BACKEND/operations/models.py b/001.BACKEND/operations/models.py index b1c1459..8000fa4 100644 --- a/001.BACKEND/operations/models.py +++ b/001.BACKEND/operations/models.py @@ -7,16 +7,31 @@ from django.utils import timezone from django.core.files.base import ContentFile from django.core.exceptions import ValidationError +class BrojacSekvence(models.Model): + """ + Tablica koja pamti isključivo zadnji iskorišteni broj za bilo koji entitet. + Broj se ovdje samo povećava i nikada se ne briše. + """ + naziv_sekvence = models.CharField(max_length=50, unique=True) # npr. 'radni_nalog_2026' + zadnji_broj = models.PositiveIntegerField(default=0) + + class Meta: + verbose_name = "Brojač sekvence" + verbose_name_plural = "Brojači sekvenci" + + def __str__(self): + return f"{self.naziv_sekvence}: {self.zadnji_broj}" + # --- FUNKCIJE ZA DINAMIČKE PUTANJE --- def putanja_slike_naloga(instance, filename): - # Kreira putanju: radni-nalozi/broj_naloga/originalni_naziv.ekstenzija - return f'radni-nalozi/{instance.broj_naloga}/{filename}' + # Čistimo broj naloga od duplih oznaka za putanju direktorija + broj = instance.broj_naloga.replace('RN-', '') if instance.broj_naloga else 'nepoznato' + return f'radni-nalozi/RN-{broj}/{filename}' def putanja_galerije_naloga(instance, filename): - # instance je objekt RadniNalogSlika koji ima ForeignKey na radni_nalog - # Sprema u isti folder gdje je i glavna slika kvara - return f'radni-nalozi/{instance.radni_nalog.broj_naloga}/galerija/{filename}' + broj = instance.radni_nalog.broj_naloga.replace('RN-', '') if instance.radni_nalog.broj_naloga else 'nepoznato' + return f'radni-nalozi/RN-{broj}/galerija/{filename}' class PutniNalog(models.Model): @@ -93,7 +108,7 @@ class RadniNalog(models.Model): class StatusRadaChoices(models.TextChoices): PLANIRANO = 'PLANIRANO', 'Planirano' U_RADU = 'U_RADU', 'U radu' - ZAVRSENO = 'ZAVRSENO', 'Završeno' + ZAVRSENO = 'ZAVRSENO', 'Završen' broj_naloga = models.CharField(max_length=20, unique=True, blank=True) @@ -137,29 +152,41 @@ class RadniNalog(models.Model): potpis_klijenta = models.ImageField(upload_to='potpisi/%Y/%m/', null=True, blank=True) def save(self, *args, **kwargs): - # 1. AUTOMATSKO GENERIRANJE BROJA NALOGA + if self.broj_naloga == "": + self.broj_naloga = None + + # 1. AUTOMATSKO GENERIRANJE BROJA NALOGA KROZ SEKVENCIJSKU TABLICU if not self.broj_naloga: godina = timezone.now().year + kljuc_sekvence = f"radni_nalog_{godina}" prefix = f"RN-{godina}-" - # Koristimo atomsku transakciju i select_for_update da spriječimo - # da dva korisnika dobiju isti broj u istoj milisekundi + # Atomska transakcija zaključava samo brojač, sprječavajući dupliranje with transaction.atomic(): - zadnji_nalog = RadniNalog.objects.select_for_update().filter( - broj_naloga__startswith=prefix - ).order_by('-broj_naloga').first() + # Dohvati ili kreiraj brojač za tekuću godinu i zaključaj redak za ažuriranje + brojac, created = BrojacSekvence.objects.select_for_update().get_or_create( + naziv_sekvence=kljuc_sekvence + ) + + # Ako je brojač tek kreiran, a u bazi već imaš povijest (npr. stigao si do 25), + # radimo automatsku sinkronizaciju da ne krene od 1 ako tablica nije prazna + if created: + zadnji_u_bazi = RadniNalog.objects.filter(broj_naloga__startswith=prefix).order_by('-broj_naloga').first() + if zadnji_u_bazi: + try: + brojac.zadnji_broj = int(zadnji_u_bazi.broj_naloga.split('-')[-1]) + except (ValueError, IndexError): + brojac.zadnji_broj = 0 - if zadnji_nalog: - try: - # RN-2026-0001 -> uzimamo "0001" - zadnji_broj_str = zadnji_nalog.broj_naloga.split('-')[-1] - novi_broj = int(zadnji_broj_str) + 1 - except (ValueError, IndexError): - novi_broj = 1 - else: - novi_broj = 1 + # Uvećaj sekvencu za 1 (Čak i ako se nalozi obrišu, ovaj broj samo raste!) + brojac.zadnji_broj += 1 + brojac.save() - self.broj_naloga = f"{prefix}{novi_broj:04d}" + # Dodijeli savršeno formatiran broj + self.broj_naloga = f"{prefix}{brojac.zadnji_broj:04d}" + else: + if self.broj_naloga.startswith("RN-RN-"): + self.broj_naloga = self.broj_naloga.replace("RN-RN-", "RN-") # 2. OBRADA SLIKE if self.slika_kvara: @@ -174,9 +201,8 @@ class RadniNalog(models.Model): # 4. LOGIKA ZA PUTNI NALOG if self.putni_nalog and self.status == self.StatusRadaChoices.ZAVRSENO: pn = self.putni_nalog - # Provjeravamo ima li nalog još uvijek otvorenih radnih naloga ostali_otvoreni = pn.radni_nalozi.exclude( - id=self.id, # Isključujemo trenutni nalog jer je on upravo završen + id=self.id, status=self.StatusRadaChoices.ZAVRSENO ).exists() @@ -200,7 +226,10 @@ class RadniNalog(models.Model): verbose_name_plural = "Radni nalozi" def __str__(self): - return f"RN-{self.broj_naloga} | {self.stroj.naziv}" + # POPRAVLJENO: Budući da polje broj_naloga već u bazi sadrži "RN-2026-0004", + # ovdje izbacujemo fiksni prefiks "RN-" kako se ne bi duplirao pri pretvaranju objekta u string! + return f"{self.broj_naloga} | {self.stroj.naziv}" + class RadniNalogSlika(models.Model): radni_nalog = models.ForeignKey( @@ -208,7 +237,6 @@ class RadniNalogSlika(models.Model): related_name='slike', on_delete=models.CASCADE ) - # POPRAVLJENO: uklonjeni navodnici oko funkcije slika = models.ImageField(upload_to=putanja_galerije_naloga, verbose_name="Slika") opis = models.CharField(max_length=100, blank=True, verbose_name="Kratki opis slike") diff --git a/001.BACKEND/operations/serializers.py b/001.BACKEND/operations/serializers.py index 81b9350..0f450ea 100644 --- a/001.BACKEND/operations/serializers.py +++ b/001.BACKEND/operations/serializers.py @@ -1,5 +1,7 @@ +from django.utils import timezone # Potrebno za kreiranje vremena polaska +from django.db import transaction # Potrebno za zaključavanje baze i sprječavanje dupliranja brojeva from rest_framework import serializers -from .models import RadniNalog, RadniNalogSlika +from .models import RadniNalog, RadniNalogSlika, PutniNalog from kupci.serializers import KupacListaSerializer from fleet.serializers import VoziloListaSerializer, StrojSerializer @@ -9,6 +11,12 @@ class RadniNalogSlikaSerializer(serializers.ModelSerializer): model = RadniNalogSlika fields = ['id', 'slika', 'opis'] +# --- POMOĆNI SERIALIZER ZA PRIKAZ VEZANOG PUTNOG NALOGA --- +class PutniNalogMinimalSerializer(serializers.ModelSerializer): + class Meta: + model = PutniNalog + fields = ['id', 'broj_naloga', 'status', 'pocetna_km'] + # --- 1. LISTA SERIALIZER --- class RadniNalogListaSerializer(serializers.ModelSerializer): klijent_naziv = serializers.CharField(source='klijent.naziv', read_only=True) @@ -33,6 +41,8 @@ class RadniNalogDetaljiSerializer(serializers.ModelSerializer): stroj = StrojSerializer(read_only=True) slike = RadniNalogSlikaSerializer(many=True, read_only=True) + putni_nalog = PutniNalogMinimalSerializer(read_only=True) + # VOZILO dohvaćamo preko putnog naloga vozilo = serializers.SerializerMethodField() @@ -50,19 +60,18 @@ class RadniNalogDetaljiSerializer(serializers.ModelSerializer): read_only_fields = ['datum_kreiranja', 'datum_azuriranja'] def get_vozilo(self, obj): - # Ako je radni nalog vezan za putni nalog, vrati podatke o vozilu if obj.putni_nalog and obj.putni_nalog.vozilo: return { "id": obj.putni_nalog.vozilo.id, "naziv": obj.putni_nalog.vozilo.naziv, "registracija": obj.putni_nalog.vozilo.registracija, - "trenutni_kilometri": obj.putni_nalog.vozilo.trenutni_kilometri + "trenutni_kilometri": obj.putni_nalog.vozilo.trenutni_kilometri, + "status_prikaz": obj.putni_nalog.vozilo.status } return None # --- 3. DEFAULT SERIALIZER --- class RadniNalogSerializer(serializers.ModelSerializer): - # Dozvoljavamo unos, ali nije obavezan ako ga backend generira broj_naloga = serializers.CharField(required=False, read_only=True) class Meta: @@ -70,17 +79,81 @@ class RadniNalogSerializer(serializers.ModelSerializer): exclude = [] def validate_broj_naloga(self, value): - # Provjera jedinstvenosti prije nego što dođe do baze if RadniNalog.objects.filter(broj_naloga=value).exists(): raise serializers.ValidationError("Radni nalog s ovim brojem već postoji.") return value def validate(self, data): - # Tvoja postojeća validacija klijenta i stroja stroj = data.get('stroj') klijent = data.get('klijent') if stroj and klijent and stroj.vlasnik != klijent: raise serializers.ValidationError( {"stroj": "Odabrani stroj ne pripada odabranom klijentu."} ) - return data \ No newline at end of file + return data + +# --- 4. KREIRANJE PUTNOG NALOGA SERIALIZER (AUTOMATSKO PROŠIRENJE SEKVENCI) --- +class KreirajPutniNalogSerializer(serializers.ModelSerializer): + radni_nalog_id = serializers.IntegerField(write_only=True) + + class Meta: + model = PutniNalog + fields = ['id', 'radni_nalog_id', 'broj_naloga', 'vozilo', 'korisnik', 'relacija', 'mjesto_odredista', 'pocetna_km', 'status'] + read_only_fields = ['id', 'broj_naloga', 'korisnik', 'relacija', 'mjesto_odredista', 'pocetna_km', 'status'] + + def validate_radni_nalog_id(self, value): + try: + nalog = RadniNalog.objects.get(id=value) + except RadniNalog.DoesNotExist: + raise serializers.ValidationError("Radni nalog s ovim ID-jem ne postoji.") + + if nalog.putni_nalog is not None: + raise serializers.ValidationError("Za ovaj radni nalog već je izrađen putni nalog.") + + if not nalog.izvrsitelj: + raise serializers.ValidationError("Radni nalog mora imati dodijeljenog izvršitelja da bi se kreirao putni nalog.") + + return value + + def create(self, validated_data): + radni_nalog = RadniNalog.objects.get(id=validated_data['radni_nalog_id']) + vozilo = validated_data.get('vozilo') + + godina = timezone.now().year + prefix = f"PN-{godina}-" + + # Koristimo atomsku transakciju i select_for_update da spriječimo trke podataka + with transaction.atomic(): + # Pronalazimo zadnji kreirani putni nalog za tekuću godinu + zadnji_pn = PutniNalog.objects.select_for_update().filter( + broj_naloga__startswith=prefix + ).order_by('-broj_naloga').first() + + if zadnji_pn: + try: + # PN-2026-0002 -> uzimamo zadnji segment "0002" i uvećavamo ga + zadnji_broj_str = zadnji_pn.broj_naloga.split('-')[-1] + novi_broj = int(zadnji_broj_str) + 1 + except (ValueError, IndexError): + novi_broj = 1 + else: + novi_broj = 1 + + # Sklapanje jedinstvenog broja putnog naloga u formatu PN-2026-XXXX + broj_pn = f"{prefix}{novi_broj:04d}" + + # Siguran upis u bazu podataka + putni_nalog = PutniNalog.objects.create( + broj_naloga=broj_pn, + vozilo=vozilo, + korisnik=radni_nalog.izvrsitelj, + relacija=f"Zagreb - {radni_nalog.klijent.grad} - Zagreb", + mjesto_odredista=radni_nalog.klijent.grad, + vrijeme_polaska=timezone.now() + ) + + # Povezujemo radni nalog na novokreirani putni nalog + radni_nalog.putni_nalog = putni_nalog + radni_nalog.save() + + return putni_nalog \ No newline at end of file diff --git a/001.BACKEND/operations/urls.py b/001.BACKEND/operations/urls.py index 4fbcecb..ea8933d 100644 --- a/001.BACKEND/operations/urls.py +++ b/001.BACKEND/operations/urls.py @@ -1,10 +1,11 @@ from django.urls import path, include from rest_framework.routers import DefaultRouter -from .views import RadniNalogViewSet +from .views import RadniNalogViewSet, PutniNalogViewSet # Kreiramo router i registriramo ViewSet za radne naloge router = DefaultRouter() router.register(r'radni-nalozi', RadniNalogViewSet, basename='radni-nalog') +router.register(r'putni-nalozi', PutniNalogViewSet, basename='putni-nalog') urlpatterns = [ # Uključujemo sve rute koje router generira diff --git a/001.BACKEND/operations/views.py b/001.BACKEND/operations/views.py index 9232581..7c2f779 100644 --- a/001.BACKEND/operations/views.py +++ b/001.BACKEND/operations/views.py @@ -1,13 +1,14 @@ from rest_framework.decorators import action from django.utils import timezone -from rest_framework import viewsets, filters, status # Dodan status +from rest_framework import viewsets, filters, permissions, status from django_filters.rest_framework import DjangoFilterBackend from .models import RadniNalog, RadniNalogSlika, PutniNalog from rest_framework.response import Response from .serializers import ( RadniNalogSerializer, RadniNalogListaSerializer, - RadniNalogDetaljiSerializer + RadniNalogDetaljiSerializer, + KreirajPutniNalogSerializer ) from kalendar.services import kreiraj_kalendarski_unos @@ -16,7 +17,6 @@ class RadniNalogViewSet(viewsets.ModelViewSet): API endpoint za upravljanje radnim nalozima. Vozilo se sada filtrira i pretražuje preko Putnog Naloga. """ - # 1. queryset: UKLONJEN 'vozilo' iz select_related queryset = RadniNalog.objects.all().select_related( 'klijent', 'izvrsitelj', 'putni_nalog__vozilo' ) @@ -29,10 +29,8 @@ class RadniNalogViewSet(viewsets.ModelViewSet): filters.OrderingFilter ] - # 2. filterset_fields: 'vozilo' zamijenjeno putanjom preko putnog naloga filterset_fields = ['status', 'izvrsitelj', 'klijent', 'putni_nalog__vozilo'] - # 3. search_fields: pretraga ide preko putni_nalog__vozilo__registracija search_fields = [ 'broj_naloga', 'klijent__naziv', @@ -51,25 +49,24 @@ class RadniNalogViewSet(viewsets.ModelViewSet): return RadniNalogSerializer def get_queryset(self): - # Optimizacija upita ovisno o akciji queryset = self.queryset if self.action == 'retrieve': queryset = queryset.prefetch_related('slike') return queryset def perform_create(self, serializer): - # Postavljamo izvršitelja ako nije poslan if not serializer.validated_data.get('izvrsitelj'): radni_nalog = serializer.save(izvrsitelj=self.request.user) else: radni_nalog = serializer.save() - # Kalendar servis try: kreiraj_kalendarski_unos(radni_nalog) except Exception as e: print(f"Greška pri kreiranju kalendarskog zapisa: {e}") + # Uredi isključivo ovu metodu unutar RadniNalogViewSet u tvom views.py + def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) @@ -82,25 +79,73 @@ class RadniNalogViewSet(viewsets.ModelViewSet): for f in files: RadniNalogSlika.objects.create(radni_nalog=instance, slika=f) - headers = self.get_success_headers(serializer.data) - return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) - - @action(detail=False, methods=['get'], url_path='sljedeci-broj') - def dohvati_sljedeci_broj(self, request): - godina = timezone.now().year - prefix = f"{godina}-" + # KLJUČNI POPRAVAK: Ponovno osvježavamo instancu iz baze podataka + # Ovo povlači čisti 'broj_naloga' nakon što ga je save() u models.py očistio od RN-RN- + instance.refresh_from_db() - zadnji_nalog = RadniNalog.objects.filter( - broj_naloga__startswith=prefix - ).order_by('-broj_naloga').first() - - if zadnji_nalog: - try: - zadnji_broj = int(zadnji_nalog.broj_naloga.split('-')[-1]) - novi_broj = zadnji_broj + 1 - except (ValueError, IndexError): - novi_broj = 1 + # Za povratni JSON koristimo detaljniji prikaz kako bi Astro odmah dobio ispravno stanje + izlazni_serializer = RadniNalogDetaljiSerializer(instance, context={'request': request}) + + headers = self.get_success_headers(izlazni_serializer.data) + return Response(izlazni_serializer.data, status=status.HTTP_201_CREATED, headers=headers) + + @action(detail=False, methods=['get'], url_path='sljedeci-broj', permission_classes=[permissions.AllowAny]) + def dohvati_sljedeci_broj(self, request): + """ + Dohvaća točan sljedeći broj radnog naloga iz neuništive sekvencijske tablice. + """ + godina = timezone.now().year + kljuc_sekvence = f"radni_nalog_{godina}" + prefix = f"RN-{godina}-" + + # Pogledaj trenutačno stanje u tablici sekvenci (bez uvećavanja) + from .models import BrojacSekvence # Lokalni uvoz ako zatreba + + brojac = BrojacSekvence.objects.filter(naziv_sekvence=kljuc_sekvence).first() + + if brojac: + sljedeci_broj = brojac.zadnji_broj + 1 else: - novi_broj = 1 + # Ako brojač još ne postoji u sekvencama, baci oko na bazu za prvu inicijalizaciju + zadnji_nalog = RadniNalog.objects.filter(broj_naloga__startswith=prefix).order_by('-broj_naloga').first() + if zadnji_nalog: + try: + sljedeci_broj = int(zadnji_nalog.broj_naloga.split('-')[-1]) + 1 + except (ValueError, IndexError): + sljedeci_broj = 1 + else: + sljedeci_broj = 1 + + sljedeci_broj_formatiran = f"{prefix}{sljedeci_broj:04d}" + return Response({"broj_naloga": sljedeci_broj_formatiran}) + +class PutniNalogViewSet(viewsets.ModelViewSet): + """ + ViewSet za upravljanje putnim nalozima. + Podržava izradu novog putnog naloga iz radnog naloga (POST) i pregled (GET). + """ + queryset = PutniNalog.objects.all().order_by('-datum_izdavanja', '-id') + permission_classes = [permissions.IsAuthenticated] # Preporučeno: Otkomentarisano radi getAuthHeaders - return Response({"broj_naloga": f"{prefix}{novi_broj:04d}"}) \ No newline at end of file + def get_serializer_class(self): + if self.action == 'create': + return KreirajPutniNalogSerializer + return KreirajPutniNalogSerializer + + def create(self, request, *args, **kwargs): + serializer = self.get_serializer_class()(data=request.data, context={'request': request}) + + if serializer.is_valid(): + putni_nalog = serializer.save() + return Response( + { + "id": putni_nalog.id, + "broj_naloga": putni_nalog.broj_naloga, + "status": putni_nalog.status, + "pocetna_km": putni_nalog.pocetna_km, + "message": "Putni nalog uspješno kreiran!" + }, + status=status.HTTP_201_CREATED + ) + + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file diff --git a/001.FRONTEND/package-lock.json b/001.FRONTEND/package-lock.json index 35eaec4..1dcc8dc 100644 --- a/001.FRONTEND/package-lock.json +++ b/001.FRONTEND/package-lock.json @@ -8,7 +8,7 @@ "name": "poslovanje", "version": "0.0.1", "dependencies": { - "@astrojs/node": "^10.0.6", + "@astrojs/node": "^10.1.1", "@tailwindcss/vite": "^4.2.4", "astro": "^6.1.9", "flowbite": "^4.0.1", @@ -32,28 +32,28 @@ } }, "node_modules/@astrojs/compiler": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-3.0.1.tgz", - "integrity": "sha512-z97oYbdebO5aoWzuJ/8q5hLK232+17KcLZ7cJ8BCWk6+qNzVxn/gftC0KzMBUTD8WAaBkPpNSQK6PXLnNrZ0CA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-4.0.0.tgz", + "integrity": "sha512-eouss7G8ygdZqHuke033VMcVw5HTZUu+PXd/h06DGDUg/jt5btPYPqh66ENWw/mU78rBrf/oeC4oqoBwMtDMNA==", "license": "MIT" }, "node_modules/@astrojs/internal-helpers": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.9.0.tgz", - "integrity": "sha512-GdYkzR26re8izmyYlBqf4z2s7zNngmWLFuxw0UKiPNqHraZGS6GKWIwSHgS22RDlu2ePFJ8bzmpBcUszut/SDg==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.9.1.tgz", + "integrity": "sha512-1pWuARqYom/TzuU3+0ZugsTrKlUydWKuULmDqSMTuonY+9IRDUEGKX/8PXQ1nBxRq3w85uGtd9q9SXfqEldMIQ==", "license": "MIT", "dependencies": { "picomatch": "^4.0.4" } }, "node_modules/@astrojs/markdown-remark": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-7.1.1.tgz", - "integrity": "sha512-C6e9BnLGlbdv6bV8MYGeHpHxsUHrCrB4OuRLqi5LI7oiBVcBcqfUN06zpwFQdHgV48QCCrMmLpyqBr7VqC+swA==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-7.1.2.tgz", + "integrity": "sha512-caXZ4Dc2St2dW8luEg22GlP0gupLdztCTQE4EzZOxW1pqWXz9mbeJEuHUkgDYcKWW8tjIHkydYDhWLVoxJ327Q==", "license": "MIT", "dependencies": { - "@astrojs/internal-helpers": "0.9.0", - "@astrojs/prism": "4.0.1", + "@astrojs/internal-helpers": "0.9.1", + "@astrojs/prism": "4.0.2", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", @@ -76,23 +76,23 @@ } }, "node_modules/@astrojs/node": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/@astrojs/node/-/node-10.0.6.tgz", - "integrity": "sha512-e8JmaP4sGxqvdei14kmBzhAqgd5/L5MTExW3Hks5DOt9LDvGzlsFZwnXVXzWPVjW/PErl7t9uLg7xWhCqfkSrA==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/@astrojs/node/-/node-10.1.1.tgz", + "integrity": "sha512-kCRbxconkgPpY4vR0GS7exovWEiCbxXLarsp+JeKixyDNf+fKN6v7jXDL8KdQgrzjhy131Kvl+GGGX8jGd8adA==", "license": "MIT", "dependencies": { - "@astrojs/internal-helpers": "0.9.0", + "@astrojs/internal-helpers": "0.9.1", "send": "^1.2.1", "server-destroy": "^1.0.1" }, "peerDependencies": { - "astro": "^6.0.0" + "astro": "^6.3.0" } }, "node_modules/@astrojs/prism": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.1.tgz", - "integrity": "sha512-nksZQVjlferuWzhPsBpQ1JE5XuKAf1id1/9Hj4a9KG4+ofrlzxUUwX4YGQF/SuDiuiGKEnzopGOt38F3AnVWsQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.2.tgz", + "integrity": "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==", "license": "MIT", "dependencies": { "prismjs": "^1.30.0" @@ -102,13 +102,12 @@ } }, "node_modules/@astrojs/telemetry": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.1.tgz", - "integrity": "sha512-7fcIxXS9J4ls5tr8b3ww9rbAIz2+HrhNJYZdkAhhB4za/I5IZ/60g+Bs8q7zwG0tOIZfNB4JWhVJ1Qkl/OrNCw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.2.tgz", + "integrity": "sha512-j8DNruA8ors99Al39RYZPJK4DC1bKkoNm93mAMuBhY9TCNC4R8n1q7ovFnJ5qhGh5Lsh7pa1gpQVpYpsJPeTHQ==", "license": "MIT", "dependencies": { "ci-info": "^4.4.0", - "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^4.0.0", "is-wsl": "^3.1.1", @@ -137,9 +136,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", "license": "MIT", "dependencies": { "@babel/types": "^7.29.0" @@ -177,25 +176,42 @@ } }, "node_modules/@clack/core": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.2.0.tgz", - "integrity": "sha512-qfxof/3T3t9DPU/Rj3OmcFyZInceqj/NVtO9rwIuJqCUgh32gwPjpFQQp/ben07qKlhpwq7GzfWpST4qdJ5Drg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.3.1.tgz", + "integrity": "sha512-fT1qHVGAag4IEkrupZ6lRRbNCs1vS9P01KB/sG8zKgvUztbYtFBtQpjSITNwooDZ83tpsPzP0mRNs1/KVszCRA==", "license": "MIT", "dependencies": { - "fast-wrap-ansi": "^0.1.3", + "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" } }, "node_modules/@clack/prompts": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.2.0.tgz", - "integrity": "sha512-4jmztR9fMqPMjz6H/UZXj0zEmE43ha1euENwkckKKel4XpSfokExPo5AiVStdHSAlHekz4d0CA/r45Ok1E4D3w==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.4.0.tgz", + "integrity": "sha512-S0My7XPGIgpRWMDG8uRqalbgT+a6FmCUdOW+HaIOVVpUPHOb7RrpvjTjiODadKp06fsrVDJZlIzc6yCTp4AnxA==", "license": "MIT", "dependencies": { - "@clack/core": "1.2.0", - "fast-string-width": "^1.1.0", - "fast-wrap-ansi": "^0.1.3", + "@clack/core": "1.3.1", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { @@ -208,6 +224,16 @@ "tslib": "^2.4.0" } }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.7", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", @@ -717,9 +743,6 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -736,9 +759,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -755,9 +775,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -774,9 +791,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -793,9 +807,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -812,9 +823,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -831,9 +839,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -850,9 +855,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -869,9 +871,6 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -894,9 +893,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -919,9 +915,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -944,9 +937,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -969,9 +959,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -994,9 +981,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1019,9 +1003,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1044,9 +1025,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1183,12 +1161,40 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, "node_modules/@oslojs/encoding": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", "license": "MIT" }, + "node_modules/@oxc-project/types": { + "version": "0.130.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.130.0.tgz", + "integrity": "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -1199,6 +1205,270 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.1.tgz", + "integrity": "sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.1.tgz", + "integrity": "sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.1.tgz", + "integrity": "sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.1.tgz", + "integrity": "sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.1.tgz", + "integrity": "sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.1.tgz", + "integrity": "sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.1.tgz", + "integrity": "sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.1.tgz", + "integrity": "sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.1.tgz", + "integrity": "sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.1.tgz", + "integrity": "sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.1.tgz", + "integrity": "sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.1.tgz", + "integrity": "sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.1.tgz", + "integrity": "sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.1.tgz", + "integrity": "sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.1.tgz", + "integrity": "sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "license": "MIT", + "peer": true + }, "node_modules/@rollup/plugin-node-resolve": { "version": "15.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz", @@ -1246,9 +1516,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", - "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz", + "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==", "cpu": [ "arm" ], @@ -1259,9 +1529,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", - "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz", + "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==", "cpu": [ "arm64" ], @@ -1272,9 +1542,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", - "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz", + "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==", "cpu": [ "arm64" ], @@ -1285,9 +1555,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", - "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz", + "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==", "cpu": [ "x64" ], @@ -1298,9 +1568,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", - "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz", + "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==", "cpu": [ "arm64" ], @@ -1311,9 +1581,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", - "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz", + "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==", "cpu": [ "x64" ], @@ -1324,15 +1594,12 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", - "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz", + "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==", "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1340,15 +1607,12 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", - "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz", + "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==", "cpu": [ "arm" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1356,15 +1620,12 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", - "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz", + "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==", "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1372,15 +1633,12 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", - "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz", + "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==", "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1388,15 +1646,12 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", - "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz", + "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==", "cpu": [ "loong64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1404,15 +1659,12 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", - "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz", + "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==", "cpu": [ "loong64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1420,15 +1672,12 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", - "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz", + "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==", "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1436,15 +1685,12 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", - "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz", + "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==", "cpu": [ "ppc64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1452,15 +1698,12 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", - "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz", + "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==", "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1468,15 +1711,12 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", - "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz", + "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==", "cpu": [ "riscv64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1484,15 +1724,12 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", - "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz", + "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==", "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1500,15 +1737,12 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", - "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz", + "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==", "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1516,15 +1750,12 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", - "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz", + "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==", "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1532,9 +1763,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", - "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz", + "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==", "cpu": [ "x64" ], @@ -1545,9 +1776,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", - "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz", + "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==", "cpu": [ "arm64" ], @@ -1558,9 +1789,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", - "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz", + "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==", "cpu": [ "arm64" ], @@ -1571,9 +1802,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", - "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz", + "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==", "cpu": [ "ia32" ], @@ -1584,9 +1815,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", - "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz", + "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==", "cpu": [ "x64" ], @@ -1597,9 +1828,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", - "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz", + "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==", "cpu": [ "x64" ], @@ -1610,13 +1841,13 @@ ] }, "node_modules/@shikijs/core": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.0.2.tgz", - "integrity": "sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.1.0.tgz", + "integrity": "sha512-jLJtSJeuFffqX6/inRE1zqU5aFv2hrszvYgq3OjbAgFRZiWv7abKMDdQzYxuSDfmUPQozZvI/kuy6VMTvnvqTQ==", "license": "MIT", "dependencies": { - "@shikijs/primitive": "4.0.2", - "@shikijs/types": "4.0.2", + "@shikijs/primitive": "4.1.0", + "@shikijs/types": "4.1.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" @@ -1626,26 +1857,26 @@ } }, "node_modules/@shikijs/engine-javascript": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.0.2.tgz", - "integrity": "sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.1.0.tgz", + "integrity": "sha512-YquhawCUgaBfhsS72e2Y/dI59gCBNPHu3fEO/tvLaXrTssxZrY5ddjtNLTwndrMgPo8b3IscE+xoICDzpTmlFQ==", "license": "MIT", "dependencies": { - "@shikijs/types": "4.0.2", + "@shikijs/types": "4.1.0", "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.4" + "oniguruma-to-es": "^4.3.6" }, "engines": { "node": ">=20" } }, "node_modules/@shikijs/engine-oniguruma": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.0.2.tgz", - "integrity": "sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.1.0.tgz", + "integrity": "sha512-axLpjVs45YBvvINa+dJF+NPW+KtFkNXsFr4SDw2BMj9GdeMnGxVB9PQb2xXlJYovslt/nz6giedAyOANkfc7hg==", "license": "MIT", "dependencies": { - "@shikijs/types": "4.0.2", + "@shikijs/types": "4.1.0", "@shikijs/vscode-textmate": "^10.0.2" }, "engines": { @@ -1653,24 +1884,24 @@ } }, "node_modules/@shikijs/langs": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.0.2.tgz", - "integrity": "sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.1.0.tgz", + "integrity": "sha512-nwOMruEkbgdZfQ/b8CgpNBVOpvG1k0N5tbmgiFeqsan401+x3ILqlzZJowSla4Agmq4hG2Uf2wh5jLTEhR8VSg==", "license": "MIT", "dependencies": { - "@shikijs/types": "4.0.2" + "@shikijs/types": "4.1.0" }, "engines": { "node": ">=20" } }, "node_modules/@shikijs/primitive": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.0.2.tgz", - "integrity": "sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.1.0.tgz", + "integrity": "sha512-zx2/2Uwj2q9X3KSyYREEhXO23xBw5WUhP4orK2lE4r+t9JGITmEe0JH+wPmJhqHpOT2bRRs6lAL945+LDvOAGw==", "license": "MIT", "dependencies": { - "@shikijs/types": "4.0.2", + "@shikijs/types": "4.1.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" }, @@ -1679,21 +1910,21 @@ } }, "node_modules/@shikijs/themes": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.0.2.tgz", - "integrity": "sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.1.0.tgz", + "integrity": "sha512-emCcTnUM7yO2wltYbaxm+yLvcCI4+h8XBKc4KmJ7EZUXoSGjcCHifkI//R4OFit9ewpg7H2/9tjOuXrT2v/Knw==", "license": "MIT", "dependencies": { - "@shikijs/types": "4.0.2" + "@shikijs/types": "4.1.0" }, "engines": { "node": ">=20" } }, "node_modules/@shikijs/types": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.0.2.tgz", - "integrity": "sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.1.0.tgz", + "integrity": "sha512-3EQWX54fMpniOrDblzAhiwiJwpiTMW6+B9DWyUd9ska483tbayFYuw47UxwuPknI31bKnySfVQ/QW+jFL4rFdA==", "license": "MIT", "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", @@ -1710,47 +1941,47 @@ "license": "MIT" }, "node_modules/@tailwindcss/node": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.4.tgz", - "integrity": "sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", + "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "^5.19.0", + "enhanced-resolve": "^5.21.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.2.4" + "tailwindcss": "4.3.0" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.4.tgz", - "integrity": "sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz", + "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", "license": "MIT", "engines": { "node": ">= 20" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.2.4", - "@tailwindcss/oxide-darwin-arm64": "4.2.4", - "@tailwindcss/oxide-darwin-x64": "4.2.4", - "@tailwindcss/oxide-freebsd-x64": "4.2.4", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.4", - "@tailwindcss/oxide-linux-arm64-gnu": "4.2.4", - "@tailwindcss/oxide-linux-arm64-musl": "4.2.4", - "@tailwindcss/oxide-linux-x64-gnu": "4.2.4", - "@tailwindcss/oxide-linux-x64-musl": "4.2.4", - "@tailwindcss/oxide-wasm32-wasi": "4.2.4", - "@tailwindcss/oxide-win32-arm64-msvc": "4.2.4", - "@tailwindcss/oxide-win32-x64-msvc": "4.2.4" + "@tailwindcss/oxide-android-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-x64": "4.3.0", + "@tailwindcss/oxide-freebsd-x64": "4.3.0", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-x64-musl": "4.3.0", + "@tailwindcss/oxide-wasm32-wasi": "4.3.0", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.4.tgz", - "integrity": "sha512-e7MOr1SAn9U8KlZzPi1ZXGZHeC5anY36qjNwmZv9pOJ8E4Q6jmD1vyEHkQFmNOIN7twGPEMXRHmitN4zCMN03g==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz", + "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", "cpu": [ "arm64" ], @@ -1764,9 +1995,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.4.tgz", - "integrity": "sha512-tSC/Kbqpz/5/o/C2sG7QvOxAKqyd10bq+ypZNf+9Fi2TvbVbv1zNpcEptcsU7DPROaSbVgUXmrzKhurFvo5eDg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz", + "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", "cpu": [ "arm64" ], @@ -1780,9 +2011,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.4.tgz", - "integrity": "sha512-yPyUXn3yO/ufR6+Kzv0t4fCg2qNr90jxXc5QqBpjlPNd0NqyDXcmQb/6weunH/MEDXW5dhyEi+agTDiqa3WsGg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz", + "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", "cpu": [ "x64" ], @@ -1796,9 +2027,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.4.tgz", - "integrity": "sha512-BoMIB4vMQtZsXdGLVc2z+P9DbETkiopogfWZKbWwM8b/1Vinbs4YcUwo+kM/KeLkX3Ygrf4/PsRndKaYhS8Eiw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz", + "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", "cpu": [ "x64" ], @@ -1812,9 +2043,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.4.tgz", - "integrity": "sha512-7pIHBLTHYRAlS7V22JNuTh33yLH4VElwKtB3bwchK/UaKUPpQ0lPQiOWcbm4V3WP2I6fNIJ23vABIvoy2izdwA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz", + "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", "cpu": [ "arm" ], @@ -1828,15 +2059,12 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.4.tgz", - "integrity": "sha512-+E4wxJ0ZGOzSH325reXTWB48l42i93kQqMvDyz5gqfRzRZ7faNhnmvlV4EPGJU3QJM/3Ab5jhJ5pCRUsKn6OQw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz", + "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1847,15 +2075,12 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.4.tgz", - "integrity": "sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz", + "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1866,15 +2091,12 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.4.tgz", - "integrity": "sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz", + "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1885,15 +2107,12 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.4.tgz", - "integrity": "sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz", + "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1904,9 +2123,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.4.tgz", - "integrity": "sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz", + "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -1921,10 +2140,10 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.8.1", - "@emnapi/runtime": "^1.8.1", - "@emnapi/wasi-threads": "^1.1.0", - "@napi-rs/wasm-runtime": "^1.1.1", + "@emnapi/core": "^1.10.0", + "@emnapi/runtime": "^1.10.0", + "@emnapi/wasi-threads": "^1.2.1", + "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, @@ -1933,9 +2152,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.4.tgz", - "integrity": "sha512-L9BXqxC4ToVgwMFqj3pmZRqyHEztulpUJzCxUtLjobMCzTPsGt1Fa9enKbOpY2iIyVtaHNeNvAK8ERP/64sqGQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", + "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", "cpu": [ "arm64" ], @@ -1949,9 +2168,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.4.tgz", - "integrity": "sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz", + "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", "cpu": [ "x64" ], @@ -1965,32 +2184,42 @@ } }, "node_modules/@tailwindcss/postcss": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.4.tgz", - "integrity": "sha512-wgAVj6nUWAolAu8YFvzT2cTBIElWHkjZwFYovF+xsqKsW2ADxM/X2opxj5NsF/qVccAOjRNe8X2IdPzMsWyHTg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.0.tgz", + "integrity": "sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w==", "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.2.4", - "@tailwindcss/oxide": "4.2.4", - "postcss": "^8.5.6", - "tailwindcss": "4.2.4" + "@tailwindcss/node": "4.3.0", + "@tailwindcss/oxide": "4.3.0", + "postcss": "^8.5.10", + "tailwindcss": "4.3.0" } }, "node_modules/@tailwindcss/vite": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.4.tgz", - "integrity": "sha512-pCvohwOCspk3ZFn6eJzrrX3g4n2JY73H6MmYC87XfGPyTty4YsCjYTMArRZm/zOI8dIt3+EcrLHAFPe5A4bgtw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz", + "integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==", "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.2.4", - "@tailwindcss/oxide": "4.2.4", - "tailwindcss": "4.2.4" + "@tailwindcss/node": "4.3.0", + "@tailwindcss/oxide": "4.3.0", + "tailwindcss": "4.3.0" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/debug": { "version": "4.1.13", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", @@ -2001,9 +2230,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "license": "MIT" }, "node_modules/@types/hast": { @@ -2052,9 +2281,9 @@ "license": "MIT" }, "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", + "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", "license": "ISC" }, "node_modules/anymatch": { @@ -2108,15 +2337,15 @@ } }, "node_modules/astro": { - "version": "6.1.9", - "resolved": "https://registry.npmjs.org/astro/-/astro-6.1.9.tgz", - "integrity": "sha512-NsAHzMzpznB281g2aM5qnBt2QjfH6ttKiZ3hSZw52If8JJ+62kbnBKbyKhR2glQcJLl7Jfe4GSl0DihFZ36rRQ==", + "version": "6.3.6", + "resolved": "https://registry.npmjs.org/astro/-/astro-6.3.6.tgz", + "integrity": "sha512-lM30gGI/iASK9Z1WQVnBBYzxVwDv8slkXbJOF7FNJdZQeBrFETpsQvYoLRupM/adt2ObP5hkYAWEeCjofoqlRw==", "license": "MIT", "dependencies": { - "@astrojs/compiler": "^3.0.1", - "@astrojs/internal-helpers": "0.9.0", - "@astrojs/markdown-remark": "7.1.1", - "@astrojs/telemetry": "3.3.1", + "@astrojs/compiler": "^4.0.0", + "@astrojs/internal-helpers": "0.9.1", + "@astrojs/markdown-remark": "7.1.2", + "@astrojs/telemetry": "3.3.2", "@capsizecss/unpack": "^4.0.0", "@clack/prompts": "^1.1.0", "@oslojs/encoding": "^1.1.0", @@ -2134,10 +2363,12 @@ "esbuild": "^0.27.3", "flattie": "^1.1.1", "fontace": "~0.4.1", + "get-tsconfig": "5.0.0-beta.4", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "js-yaml": "^4.1.1", + "jsonc-parser": "^3.3.1", "magic-string": "^0.30.21", "magicast": "^0.5.2", "mrmime": "^2.0.1", @@ -2156,7 +2387,6 @@ "tinyclip": "^0.1.12", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", - "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", "unifont": "~0.7.4", "unist-util-visit": "^5.1.0", @@ -2184,6 +2414,80 @@ "sharp": "^0.34.0" } }, + "node_modules/astro/node_modules/vite": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz", + "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -2497,9 +2801,9 @@ } }, "node_modules/devalue": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.7.1.tgz", - "integrity": "sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA==", + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz", + "integrity": "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==", "license": "MIT" }, "node_modules/devlop": { @@ -2524,12 +2828,6 @@ "node": ">=0.3.1" } }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -2622,9 +2920,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.0.tgz", - "integrity": "sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==", + "version": "5.21.6", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz", + "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -2748,27 +3046,27 @@ "license": "MIT" }, "node_modules/fast-string-truncated-width": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-1.2.1.tgz", - "integrity": "sha512-Q9acT/+Uu3GwGj+5w/zsGuQjh9O1TyywhIwAxHudtWrgF09nHOPrvTLhQevPbttcxjr/SNN7mJmfOw/B1bXgow==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", "license": "MIT" }, "node_modules/fast-string-width": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-1.1.0.tgz", - "integrity": "sha512-O3fwIVIH5gKB38QNbdg+3760ZmGz0SZMgvwJbA1b2TGXceKE6A2cOlfogh1iw8lr049zPyd7YADHy+B7U4W9bQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", "license": "MIT", "dependencies": { - "fast-string-truncated-width": "^1.2.0" + "fast-string-truncated-width": "^3.0.2" } }, "node_modules/fast-wrap-ansi": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.1.6.tgz", - "integrity": "sha512-HlUwET7a5gqjURj70D5jl7aC3Zmy4weA1SHUfM0JFI0Ptq987NH2TwbBFLoERhfwk+E+eaq4EK3jXoT+R3yp3w==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", "license": "MIT", "dependencies": { - "fast-string-width": "^1.1.0" + "fast-string-width": "^3.0.2" } }, "node_modules/fdir": { @@ -2798,9 +3096,9 @@ } }, "node_modules/flowbite": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flowbite/-/flowbite-4.0.1.tgz", - "integrity": "sha512-UwUjvnqrQTiFm3uMJ0WWnzKXKoDyNyfyEzoNnxmZo6KyDzCedjqZw1UW0Oqdn+E0iYVdPu0fizydJN6e4pP9Rw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/flowbite/-/flowbite-4.0.2.tgz", + "integrity": "sha512-TxBdfZpd3HktHH4ashiYjirrSZeVPtG1OCRZMKTeXUa9FOlMxSF98zgjMB/AxE49KZ+rlfgWynAlaNpWxrqZmA==", "license": "MIT", "dependencies": { "@popperjs/core": "^2.9.3", @@ -2873,6 +3171,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-tsconfig": { + "version": "5.0.0-beta.4", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.4.tgz", + "integrity": "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "engines": { + "node": ">=20.20.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/github-slugger": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", @@ -3149,12 +3462,12 @@ } }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -3245,9 +3558,9 @@ } }, "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -3265,6 +3578,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" + }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", @@ -3401,9 +3720,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -3424,9 +3740,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -3447,9 +3760,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -3470,9 +3780,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -3537,9 +3844,9 @@ } }, "node_modules/lru-cache": { - "version": "11.3.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", - "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz", + "integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -3555,12 +3862,12 @@ } }, "node_modules/magicast": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", - "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz", + "integrity": "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", + "@babel/parser": "^7.29.3", "@babel/types": "^7.29.0", "source-map-js": "^1.2.1" } @@ -4419,9 +4726,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "funding": [ { "type": "github", @@ -4563,9 +4870,9 @@ } }, "node_modules/p-queue": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.2.0.tgz", - "integrity": "sha512-dWgLE8AH0HjQ9fe74pUkKkvzzYT18Inp4zra3lKHnnwqGvcfcUBrvF2EAVX+envufDNBOzpPq/IBUONDbI7+3g==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.0.tgz", + "integrity": "sha512-7NED7xhQ74Ngp4JP/2e0VZHp7vSWfJfqeiR92jPgxsz6m0Se4P03YoTKa9dDXyZ3r6P616gUXttrB6nnHYKang==", "license": "MIT", "dependencies": { "eventemitter3": "^5.0.4", @@ -4666,9 +4973,9 @@ } }, "node_modules/postcss": { - "version": "8.5.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", - "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "funding": [ { "type": "opencollective", @@ -4685,7 +4992,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -4927,6 +5234,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/retext": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", @@ -4988,10 +5304,44 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/rolldown": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz", + "integrity": "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@oxc-project/types": "=0.130.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.1", + "@rolldown/binding-darwin-arm64": "1.0.1", + "@rolldown/binding-darwin-x64": "1.0.1", + "@rolldown/binding-freebsd-x64": "1.0.1", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.1", + "@rolldown/binding-linux-arm64-gnu": "1.0.1", + "@rolldown/binding-linux-arm64-musl": "1.0.1", + "@rolldown/binding-linux-ppc64-gnu": "1.0.1", + "@rolldown/binding-linux-s390x-gnu": "1.0.1", + "@rolldown/binding-linux-x64-gnu": "1.0.1", + "@rolldown/binding-linux-x64-musl": "1.0.1", + "@rolldown/binding-openharmony-arm64": "1.0.1", + "@rolldown/binding-wasm32-wasi": "1.0.1", + "@rolldown/binding-win32-arm64-msvc": "1.0.1", + "@rolldown/binding-win32-x64-msvc": "1.0.1" + } + }, "node_modules/rollup": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz", - "integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==", + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz", + "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==", "license": "MIT", "dependencies": { "@types/estree": "1.0.8" @@ -5004,34 +5354,40 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.2", - "@rollup/rollup-android-arm64": "4.60.2", - "@rollup/rollup-darwin-arm64": "4.60.2", - "@rollup/rollup-darwin-x64": "4.60.2", - "@rollup/rollup-freebsd-arm64": "4.60.2", - "@rollup/rollup-freebsd-x64": "4.60.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", - "@rollup/rollup-linux-arm-musleabihf": "4.60.2", - "@rollup/rollup-linux-arm64-gnu": "4.60.2", - "@rollup/rollup-linux-arm64-musl": "4.60.2", - "@rollup/rollup-linux-loong64-gnu": "4.60.2", - "@rollup/rollup-linux-loong64-musl": "4.60.2", - "@rollup/rollup-linux-ppc64-gnu": "4.60.2", - "@rollup/rollup-linux-ppc64-musl": "4.60.2", - "@rollup/rollup-linux-riscv64-gnu": "4.60.2", - "@rollup/rollup-linux-riscv64-musl": "4.60.2", - "@rollup/rollup-linux-s390x-gnu": "4.60.2", - "@rollup/rollup-linux-x64-gnu": "4.60.2", - "@rollup/rollup-linux-x64-musl": "4.60.2", - "@rollup/rollup-openbsd-x64": "4.60.2", - "@rollup/rollup-openharmony-arm64": "4.60.2", - "@rollup/rollup-win32-arm64-msvc": "4.60.2", - "@rollup/rollup-win32-ia32-msvc": "4.60.2", - "@rollup/rollup-win32-x64-gnu": "4.60.2", - "@rollup/rollup-win32-x64-msvc": "4.60.2", + "@rollup/rollup-android-arm-eabi": "4.60.4", + "@rollup/rollup-android-arm64": "4.60.4", + "@rollup/rollup-darwin-arm64": "4.60.4", + "@rollup/rollup-darwin-x64": "4.60.4", + "@rollup/rollup-freebsd-arm64": "4.60.4", + "@rollup/rollup-freebsd-x64": "4.60.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", + "@rollup/rollup-linux-arm-musleabihf": "4.60.4", + "@rollup/rollup-linux-arm64-gnu": "4.60.4", + "@rollup/rollup-linux-arm64-musl": "4.60.4", + "@rollup/rollup-linux-loong64-gnu": "4.60.4", + "@rollup/rollup-linux-loong64-musl": "4.60.4", + "@rollup/rollup-linux-ppc64-gnu": "4.60.4", + "@rollup/rollup-linux-ppc64-musl": "4.60.4", + "@rollup/rollup-linux-riscv64-gnu": "4.60.4", + "@rollup/rollup-linux-riscv64-musl": "4.60.4", + "@rollup/rollup-linux-s390x-gnu": "4.60.4", + "@rollup/rollup-linux-x64-gnu": "4.60.4", + "@rollup/rollup-linux-x64-musl": "4.60.4", + "@rollup/rollup-openbsd-x64": "4.60.4", + "@rollup/rollup-openharmony-arm64": "4.60.4", + "@rollup/rollup-win32-arm64-msvc": "4.60.4", + "@rollup/rollup-win32-ia32-msvc": "4.60.4", + "@rollup/rollup-win32-x64-gnu": "4.60.4", + "@rollup/rollup-win32-x64-msvc": "4.60.4", "fsevents": "~2.3.2" } }, + "node_modules/rollup/node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, "node_modules/sax": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", @@ -5042,9 +5398,9 @@ } }, "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -5137,17 +5493,17 @@ } }, "node_modules/shiki": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.0.2.tgz", - "integrity": "sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.1.0.tgz", + "integrity": "sha512-l/ABZPUR5v70jI10EzqfMS/I96vjSGv2y0ihUV+WYFzv0EfvW4s54m0Lg8wCrrL+2IkwBzFTuxkZjPf8b2NX9Q==", "license": "MIT", "dependencies": { - "@shikijs/core": "4.0.2", - "@shikijs/engine-javascript": "4.0.2", - "@shikijs/engine-oniguruma": "4.0.2", - "@shikijs/langs": "4.0.2", - "@shikijs/themes": "4.0.2", - "@shikijs/types": "4.0.2", + "@shikijs/core": "4.1.0", + "@shikijs/engine-javascript": "4.1.0", + "@shikijs/engine-oniguruma": "4.1.0", + "@shikijs/langs": "4.1.0", + "@shikijs/themes": "4.1.0", + "@shikijs/types": "4.1.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" }, @@ -5253,9 +5609,9 @@ } }, "node_modules/tailwindcss": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.4.tgz", - "integrity": "sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", + "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", "license": "MIT" }, "node_modules/tapable": { @@ -5287,9 +5643,9 @@ } }, "node_modules/tinyexec": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", - "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", "license": "MIT", "engines": { "node": ">=18" @@ -5340,26 +5696,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/tsconfck": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", - "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -5368,9 +5704,9 @@ "optional": true }, "node_modules/ufo": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", - "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", "license": "MIT" }, "node_modules/ultrahtml": { @@ -5677,17 +6013,17 @@ } }, "node_modules/vite": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", - "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.13.tgz", + "integrity": "sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==", "license": "MIT", + "peer": true, "dependencies": { - "esbuild": "^0.27.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.14", + "rolldown": "1.0.1", + "tinyglobby": "^0.2.16" }, "bin": { "vite": "bin/vite.js" @@ -5703,9 +6039,10 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.18", + "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", - "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", @@ -5718,15 +6055,18 @@ "@types/node": { "optional": true }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, "jiti": { "optional": true }, "less": { "optional": true }, - "lightningcss": { - "optional": true - }, "sass": { "optional": true }, @@ -5816,9 +6156,9 @@ } }, "node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/001.FRONTEND/package.json b/001.FRONTEND/package.json index c492139..05fbd2e 100644 --- a/001.FRONTEND/package.json +++ b/001.FRONTEND/package.json @@ -6,10 +6,11 @@ "node": ">=22.12.0" }, "scripts": { - "dev": "astro dev", + "dev": "astro dev --host", "build": "astro build", "preview": "astro preview", - "astro": "astro" + "astro": "astro", + "start": "node ./dist/server/entry.mjs" }, "dependencies": { "@astrojs/node": "^10.0.6", diff --git a/001.FRONTEND/src/components/AkcijePanel.astro b/001.FRONTEND/src/components/AkcijePanel.astro index e66d100..7746efc 100644 --- a/001.FRONTEND/src/components/AkcijePanel.astro +++ b/001.FRONTEND/src/components/AkcijePanel.astro @@ -4,7 +4,7 @@ import Button from './Button.astro'; interface Props { - tip: 'dashboard' | 'radni-nalog' | 'stroj' | 'vlasnik'; + tip: 'dashboard' | 'radni-nalog' | 'stroj' | 'vlasnik' | 'vozilo'; // DODANO: vozilo podaci: any; } @@ -16,117 +16,138 @@ const formatDate = (date: string) => --- \ No newline at end of file diff --git a/001.FRONTEND/src/components/Kalendar/Kalendar.astro b/001.FRONTEND/src/components/Kalendar/Kalendar.astro index 789f683..4fc923d 100644 --- a/001.FRONTEND/src/components/Kalendar/Kalendar.astro +++ b/001.FRONTEND/src/components/Kalendar/Kalendar.astro @@ -8,9 +8,26 @@ const apiEvents = await fetchCalendarEvents(); // 2. Grupiranje događaja po datumu (YYYY-MM-DD) const eventsByDate = apiEvents.reduce((acc, event) => { + if (!event.start) return acc; + const dateKey = new Date(event.start).toISOString().split('T')[0]; if (!acc[dateKey]) acc[dateKey] = []; - acc[dateKey].push(event); + + // Koristimo čista, razdvojena polja s novog Django serializera + acc[dateKey].push({ + id: event.id, + title: event.title, + opis: event.opis_cisti, // Čisti opis kvara s backenda + start: event.start, + tip: (event.tip || event.status || 'planirano').toLowerCase(), + radni_nalog: event.radni_nalog || event.id, + je_radni_nalog: event.je_radni_nalog !== undefined ? event.je_radni_nalog : true, + izvrsitelj_ime: event.izvrsitelj_ime, + klijent: event.klijent || null, + stroj: event.stroj || null, + vozilo: event.vozilo_naziv // Čisti naziv vozila s backenda + }); + return acc; }, {}); @@ -70,18 +87,13 @@ const currentYearMonth = "2026-05";
-
+ \ No newline at end of file diff --git a/001.FRONTEND/src/lib/api.js b/001.FRONTEND/src/lib/api.js new file mode 100644 index 0000000..0cae328 --- /dev/null +++ b/001.FRONTEND/src/lib/api.js @@ -0,0 +1,468 @@ +// src/lib/api.js +const API_BASE = import.meta.env.PUBLIC_API_URL; + +/** + * POMOĆNA FUNKCIJA:端 Dohvaća token i postavlja Headere + */ +function getAuthHeaders(bodyData = {}) { + const token = typeof window !== 'undefined' ? localStorage.getItem('access_token') : null; + const headers = {}; + + if (token) { + headers['Authorization'] = `Bearer ${token}`; // Usklađeno sa Simple JWT standardom + } + + // Provjera je li bodyData FormData (za slike/naloge) + const isFormData = bodyData instanceof FormData; + + // Ako NIJE FormData, šaljemo JSON + if (!isFormData) { + headers['Content-Type'] = 'application/json'; + } + + return headers; +} +/** + * Centralizirana obrada odgovora s Toast podrškom + */ +async function handleResponse(res) { + if (res.status === 401) { + console.warn("Token istekao ili je nevažeći."); + } + + if (!res.ok) { + const errorData = await res.json().catch(() => ({})); + console.error("API Error Response:", errorData); + + let msg = "Greška pri sinkronizaciji podataka."; + + if (errorData.detail) { + msg = errorData.detail; + } + else if (typeof errorData === 'object' && errorData !== null) { + const kljuceviGresaka = Object.keys(errorData); + + if (kljuceviGresaka.length > 0) { + const prvoPolje = kljuceviGresaka[0]; + const greskaVrijednost = errorData[prvoPolje]; + + if (Array.isArray(greskaVrijednost) && greskaVrijednost.length > 0) { + msg = `${prvoPolje}: ${greskaVrijednost[0]}`; + } else if (typeof greskaVrijednost === 'string') { + msg = `${prvoPolje}: ${greskaVrijednost}`; + } + } + } + + if (typeof window !== 'undefined') { + window.showToast?.(msg, "error"); + } + return null; + } + + // VRAĆA PARSIRAN OBJEKT - Stream je zatvoren nakon ove linije! + return await res.json(); +} + +// --- RUTE --- +export const routes = { + // Autentifikacija + login: () => '/token/', + trenutniKorisnik: () => '/users/me/', + + // Radni nalozi + radniNalozi: (params = {}) => { + const baseUrl = '/operativa/radni-nalozi/'; + // Čistimo null i undefined vrijednosti iz filtera + const cleanParams = Object.fromEntries( + Object.entries(params).filter(([_, v]) => v != null) + ); + const queryString = new URLSearchParams(cleanParams).toString(); + return queryString ? `${baseUrl}?${queryString}` : baseUrl; + }, + radniNalogDetalji: (id) => `/operativa/radni-nalozi/${id}/`, + sljedeciBrojNaloga: () => '/operativa/radni-nalozi/sljedeci-broj/', + + // Putni nalozi i logistika + putniNalozi: () => '/operativa/putni-nalozi/', + + // Vozila i strojevi (Fleet modul) + vozila: () => '/fleet/vozila/', // USKLAĐENO: Prebačeno pod zajednički /fleet/ namespace + strojevi: (vlasnikId = null) => { + const baseUrl = '/fleet/strojevi/'; + if (vlasnikId) return `${baseUrl}?vlasnik=${vlasnikId}`; + return baseUrl; + }, + strojDetalji: (id) => `/fleet/strojevi/${id}/`, + + // Kupci / Klijenti + kupciSvi: () => '/kupci/svi/', + kupacDetalji: (id) => `/kupci/svi/${id}/`, + + // Kalendar i raspored + mojRaspored: () => '/kalendar/moj-raspored/', + kalendarDogadaji: () => '/kalendar/dogadaji/' +}; + +// --- API METODE --- + +/** + * Dohvat profila trenutno prijavljenog korisnika + */ +export async function fetchCurrentUser() { + try { + const url = `${API_BASE}${routes.trenutniKorisnik()}`; + const res = await fetch(url, { + method: 'GET', + headers: getAuthHeaders() + }); + return await handleResponse(res); + } catch (e) { + console.error("fetchCurrentUser Failure:", e); + return null; + } +} + +/** + * Dohvat i filtriranje radnih naloga (za tablice i liste u operativi) + */ +export async function fetchRadniNalozi(params = {}) { + try { + // Logika čišćenja i slaganja query parametara je sada delegirana routes objektu! + const url = `${API_BASE}${routes.radniNalozi(params)}`; + + const res = await fetch(url, { + method: 'GET', + headers: getAuthHeaders() + }); + return await handleResponse(res); + } catch (e) { + console.error("fetchRadniNalozi Failure:", e); + return []; + } +} + +/** + * Paralelni dohvat podataka za glavno dispečersko sučelje (Dashboard flote i naloga) + */ +export async function fetchDashboardData() { + try { + // Dinamički povlačimo staze iz routes objekta za paralelno okidanje + const urlVozila = `${API_BASE}${routes.vozila()}`; + const urlNalozi = `${API_BASE}${routes.radniNalozi()}`; + + const [resV, resN] = await Promise.all([ + fetch(urlVozila, { method: 'GET', headers: getAuthHeaders() }), + fetch(urlNalozi, { method: 'GET', headers: getAuthHeaders() }) + ]); + + // Budući da Dashboard podatke često renderiraš u paralelnim karticama, + // koristimo brzi i sigurni fallback u slučaju prazne baze ili neočekivanog formata + const vozilaData = resV.ok ? await resV.json().catch(() => []) : []; + const naloziData = resN.ok ? await resN.json().catch(() => []) : []; + + return { + vozila: Array.isArray(vozilaData) ? vozilaData : (vozilaData.results || []), + nalozi: Array.isArray(naloziData) ? naloziData : (naloziData.results || []) + }; + } catch (e) { + console.error("FetchDashboardData Failure:", e); + return { vozila: [], nalozi: [] }; + } +} + +/** + * Prijava korisnika i pohrana JWT tokena + */ +export async function login(email, password) { + try { + // Povlačenje centralizirane rute za token + const url = `${API_BASE}${routes.login()}`; + + const res = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, password }) + }); + + const data = await res.json(); + if (!res.ok) throw new Error(data.detail || "Neuspješna prijava"); + + localStorage.setItem('access_token', data.access); + localStorage.setItem('refresh_token', data.refresh); + return { success: true }; + } catch (e) { + console.error("Login Error:", e); + return { success: false, error: e.message }; + } +} + +/** + * Odjava korisnika i čišćenje lokalne pohrane + */ +export function logout() { + localStorage.removeItem('access_token'); + localStorage.removeItem('refresh_token'); + if (typeof window !== 'undefined') window.location.href = '/login'; +} + +/** + * Dohvat detalja pojedinačnog kupca/klijenta + */ +export async function fetchKupacDetalji(id) { + try { + const url = `${API_BASE}${routes.kupacDetalji(id)}`; + const res = await fetch(url, { + method: 'GET', + headers: getAuthHeaders() + }); + return await handleResponse(res); + } catch (e) { + console.error(`Greška u fetchKupacDetalji za ID ${id}:`, e); + return null; + } +} + +/** + * Unos novog servisnog vozila u bazu flote + */ +export async function createVozilo(payload) { + try { + const url = `${API_BASE}${routes.vozila()}`; + const res = await fetch(url, { + method: 'POST', + headers: getAuthHeaders(), + body: JSON.stringify(payload) + }); + + const data = await handleResponse(res); + if (data && typeof window !== 'undefined') { + window.showToast?.("Vozilo uspješno uneseno!", "success"); + } + return data; + } catch (e) { + console.error("Greška u createVozilo:", e); + return null; + } +} + +/** + * Kreiranje novog radnog naloga (šalje se FormData zbog učitavanja slika s terena) + */ +export async function createNalog(formData) { + try { + // Koristimo bazičnu rutu bez parametara za POST zahtjev + const url = `${API_BASE}${routes.radniNalozi()}`; + const res = await fetch(url, { + method: 'POST', + headers: getAuthHeaders(formData), // Automatski izbacuje Content-Type za FormData + body: formData + }); + return await handleResponse(res); + } catch (e) { + console.error("Greška u createNalog:", e); + return null; + } +} + +/** + * Parcijalno ažuriranje radnog naloga (npr. promjena statusa, dodavanje opisa) preko centralizirane rute + */ +export async function patchNalog(id, data) { + try { + // Koristimo novu stazu za detalje naloga iz routes objekta + const url = `${API_BASE}${routes.radniNalogDetalji(id)}`; + + const res = await fetch(url, { + method: 'PATCH', + headers: getAuthHeaders(), + body: JSON.stringify(data) + }); + + return await handleResponse(res); + } catch (e) { + console.error(`Greška u patchNalog za ID ${id}:`, e); + return null; + } +} + +/** + * Dohvat svih kalendarskih događaja (planirani servisi, atesti, tereni) + */ +export async function fetchCalendarEvents() { + try { + // Koristimo novu stazu iz routes objekta + const url = `${API_BASE}${routes.kalendarDogadaji()}`; + + const res = await fetch(url, { + method: 'GET', + headers: getAuthHeaders() + }); + + // Centralizirana obrada odgovora s Toast error handlingom + const data = await handleResponse(res); + + if (!data) return []; // Siguran fallback ako zahtjev baci grešku + + return Array.isArray(data) ? data : (data.results || []); + } catch (e) { + console.error("fetchCalendarEvents Failure:", e); + return []; + } +} + +/** + * Dohvat osobnog rasporeda servisera iz kalendara + */ +export async function fetchMojRaspored() { + try { + // Koristimo stazu iz routes objekta + const url = `${API_BASE}${routes.mojRaspored()}`; + + const res = await fetch(url, { + method: 'GET', + headers: getAuthHeaders() + }); + + // Centralizirana obrada odgovora s Toast error handlingom + const data = await handleResponse(res); + + if (!data) return []; // Siguran fallback na prazan niz ako zahtjev ne prođe + + return Array.isArray(data) ? data : (data.results || []); + } catch (e) { + console.error("fetchMojRaspored Failure:", e); + return []; + } +} + +/** + * Dohvat popisa svih kupaca/klijenata za dropdown u novom nalogu + */ +export async function fetchKupciData() { + try { + // Koristimo stazu iz routes objekta + const url = `${API_BASE}${routes.kupciSvi()}`; + + const res = await fetch(url, { + method: 'GET', + headers: getAuthHeaders() + }); + + // Centralizirana obrada odgovora + const data = await handleResponse(res); + + if (!data) return { kupci: [] }; // Fallback ako je token nevažeći + + // Vraćamo objekt u formatu koji novi.astro destrukturira na serveru + return { + kupci: Array.isArray(data) ? data : (data.results || []) + }; + } catch (e) { + console.error("fetchKupciData Failure:", e); + return { kupci: [] }; + } +} + +/** + * Dohvat popisa strojeva (opcionalno filtrirano po vlasniku) pomoću centralizirane rute + */ +export async function fetchStrojeviData(vlasnikId = null) { + try { + // Generiramo ispravan URL preko routes objekta + const url = `${API_BASE}${routes.strojevi(vlasnikId)}`; + + const res = await fetch(url, { + method: 'GET', + headers: getAuthHeaders() + }); + + // Koristimo centraliziranu obradu odgovora (s Toast podrškom) + const data = await handleResponse(res); + + if (!data) return { strojevi: [] }; // Siguran fallback ako je zahtjev prekinut (401, 500...) + + // Vraćamo objekt sa strojevima prateći strukturu koju novi.astro očekuje (.map destructuring) + return { + strojevi: Array.isArray(data) ? data : (data.results || []) + }; + } catch (e) { + console.error("fetchStrojeviData Failure:", e); + return { strojevi: [] }; + } +} + +/** + * Dohvat popisa svih putnih naloga pomoću centralizirane rute i handleResponse-a + */ +export async function fetchPutniNaloziData() { + try { + // Koristimo zajedničku stazu iz routes objekta + const url = `${API_BASE}${routes.putniNalozi()}`; + + const res = await fetch(url, { + method: 'GET', + headers: getAuthHeaders() + }); + + // Koristimo centraliziranu obradu odgovora (s Toast error handlingom) + const data = await handleResponse(res); + + if (!data) return []; // Fallback ako je handleResponse presreo grešku i vratio null + + // Provjera vraća li Django čistu listu ili paginirani rezultatski objekt (results) + return Array.isArray(data) ? data : (data.results || []); + } catch (e) { + console.error("Greška pri dohvatu putnih naloga:", e); + return []; + } +} + +export async function fetchSljedeciBrojNaloga() { + try { + // Koristimo novu rutu iz routes objekta + const url = `${API_BASE}${routes.sljedeciBrojNaloga()}`; + + const res = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }); + + if (res.ok) { + return await res.json(); + } else { + console.error("Greška na backendu pri dohvaćanju brojača:", res.status); + return { broj_naloga: "RN-2026-XXXX" }; + } + } catch (err) { + console.error("Mrežna greška u fetchSljedeciBrojNaloga:", err); + return { broj_naloga: "RN-2026-XXXX" }; + } +} + +/** + * Kreiranje novog putnog naloga - S koso crtom (trailing slash) za Django usklađenost + */ +export async function createPutniNalog(radniNalogId, voziloId) { + try { + // Koristimo novu stazu iz routes objekta + const url = `${API_BASE}${routes.putniNalozi()}`; + + const res = await fetch(url, { + method: 'POST', + headers: getAuthHeaders(), + body: JSON.stringify({ + radni_nalog_id: parseInt(radniNalogId, 10), + vozilo: parseInt(voziloId, 10) + }) + }); + + // Vraća gotov JSON objekt (ili null ako je toast okinuo grešku) + return await handleResponse(res); + } catch (e) { + console.error("createPutniNalog krah:", e); + return null; + } +} \ No newline at end of file diff --git a/001.FRONTEND/src/pages/fleet/vozila/[id].astro b/001.FRONTEND/src/pages/fleet/vozila/[id].astro new file mode 100644 index 0000000..668d687 --- /dev/null +++ b/001.FRONTEND/src/pages/fleet/vozila/[id].astro @@ -0,0 +1,156 @@ +--- +// src/pages/fleet/vozila/[id].astro +import Layout from "../../../layouts/Layout.astro"; // Prilagodi putanju svom glavnom layoutu +import { fetchCalendarEvents } from "../../../lib/api"; // Pretpostavka lokacije tvog centraliziranog API-ja +import { getStatusColorClass } from "../../../utils/ui"; + +// 1. Dohvat ID-ja iz URL parametara +const { id } = Astro.params; + +// 2. Dohvat podataka s Django API-ja +// Ovdje koristimo tvoj centralizirani API klijent. Ako imaš fetchVoziloDetails(id), iskoristi ga. +// Kao siguran fallback, dohvaćamo sva vozila i filtriramo ono koje nam treba. +const response = await fetch(`${Astro.url.origin}/api/fleet/vozila`); // ili izravno tvoj DRF endpoint ako lib nema pojedinačni fetch +let vozilo = null; + +try { + // Simulacija dohvata preko tvog centraliziranog klijenta ili izravnog fetch-a + // Ako tvoj api.js ima metodu npr. fetchVozilo(id), zamijeni ovo s: vozilo = await fetchVozilo(id); + const svaVozilaResponse = await fetch("http://127.0.0.1:8000/api/fleet/vozila/"); // Primjer internog DRF endpointa + const vozila = await svaVozilaResponse.json(); + vozilo = vozila.find((v) => v.id === parseInt(id)); +} catch (error) { + console.error("Greška pri dohvaćanju podataka o vozilu:", error); +} + +// Ako vozilo ne postoji, preusmjeravamo na 404 ili na listu vozila +if (!vozilo) { + return Astro.redirect("/fleet/vozila?error=not-found"); +} + +// Pomoćna funkcija za formatiranje statusa lokalno ako zakaže status_prikaz +function formatStatusLocal(status) { + const map = { + 'aktivan': 'Aktivan', + 'servis': 'Na servisu', + 'neaktivan': 'Izvan pogona' + }; + return map[status?.toLowerCase()] || status; +} + +const statusKlasa = getStatusColorClass(vozilo.status?.toLowerCase()); +--- + + +
+ + + Povratak na popis + + +
+ +
+
+
+ +
+
+

+ {vozilo.naziv} +

+ + Interni ID: #{vozilo.id} + +
+
+ + + {vozilo.status_prikaz || formatStatusLocal(vozilo.status)} + +
+ +
+ +
+ + Registracijska oznaka + +
+ + + {vozilo.registracija} + +
+
+ +
+ + Trenutna kilometraža + +
+ + + {vozilo.trenutni_kilometri?.toLocaleString('hr-HR') || 0} KM + +
+
+ +
+
+ +
+ + Trenutni raspored / Baza + +

+ Terenska Baza +

+

+ Vozilo je mapirano na centralni logistički sustav fleet managementa. Sve izmjene kilometara i servisnih naloga sinkroniziraju se u realnom vremenu s radnim nalozima operativnog tima. +

+
+ +
+ +
+ + Radni Nalozi + + +
+ +
+
+
+ + \ No newline at end of file diff --git a/001.FRONTEND/src/pages/fleet/vozila/index.astro b/001.FRONTEND/src/pages/fleet/vozila/index.astro index d434eee..bd17ced 100644 --- a/001.FRONTEND/src/pages/fleet/vozila/index.astro +++ b/001.FRONTEND/src/pages/fleet/vozila/index.astro @@ -3,16 +3,18 @@ import Layout from "../../../layouts/Layout.astro"; import Button from "../../../components/Button.astro"; import AkcijePanel from "../../../components/AkcijePanel.astro"; -import WelcomeHeader from "../../../components/WelcomeHeader.astro"; // Importamo novi header +import WelcomeHeader from "../../../components/WelcomeHeader.astro"; import NaslovList from "../../../components/NaslovList.astro"; import GenericKarticaItem from "../../../components/GenericKarticaItem.astro"; -// Importiranje utilitija +// Importiranje utilitija i centraliziranog API-ja import { fetchDashboardData, fetchCurrentUser } from "../../../lib/api"; -import { getStatusColorClass } from "../../../utils/ui"; +import { getStatusColorClass, formatStatus } from "../../../utils/ui"; -// 1. DOHVAT PODATAKA -// Paralelno dohvaćamo podatke o vozilima i trenutnom korisniku za Header +// 1. DOHVAT FILTERA IZ URL-a (npr. ?status=servis) +const statusFilter = Astro.url.searchParams.get('status')?.toLowerCase(); + +// 2. DOHVAT PODATAKA const [dashboardData, user] = await Promise.all([ fetchDashboardData(), fetchCurrentUser() @@ -20,66 +22,75 @@ const [dashboardData, user] = await Promise.all([ const vozila = dashboardData?.vozila || []; -// 2. LOGIKA STATISTIKE -const aktivnaVozila = vozila.filter(v => v.status === 'aktivan').length; -const naServisu = vozila.filter(v => v.status === 'servis').length; -const neaktivnaVozila = vozila.filter(v => v.status === 'neaktivan').length; +// 3. LOGIKA STATISTIKE (Uvijek se računa iz originalnog niza, neovisno o filteru) +const aktivnaVozila = vozila.filter(v => v.status?.toLowerCase() === 'aktivan').length; +const naServisu = vozila.filter(v => v.status?.toLowerCase() === 'servis').length; +const neaktivnaVozila = vozila.filter(v => v.status?.toLowerCase() === 'neaktivan').length; -// Podaci za WelcomeHeader -const imeKorisnika = user?.first_name || "Serviser"; -// const ulogaKorisnika = user?.is_serviser ? "Terenski Tehničar" : "Logistika"; +// 4. LOGIKA FILTRIRANJA (Serverska strana) +let filtriraniVozila = vozila; +if (statusFilter) { + filtriraniVozila = vozila.filter(v => v.status?.toLowerCase() === statusFilter); +} + +// Možeš dodati limit kroz props ako ovu stranicu ikada budeš koristio kao parcijalnu komponentu +const limit = 0; +const prikazanaVozila = limit > 0 ? filtriraniVozila.slice(0, limit) : filtriraniVozila; + +// Dinamički naslov liste ovisno o odabranom filteru flote +const prikazaniNaslov = statusFilter + ? `Flota: ${formatStatus(statusFilter)}` + : "Aktivna Flota"; ---
- -
-
- {vozila.length > 0 ? vozila.map((v) => ( + {prikazanaVozila.length > 0 ? prikazanaVozila.map((v) => ( )) : (
-

Baza podataka je prazna

- +

Nema vozila za odabrani status

+ {!statusFilter && ( + + )}
)}
-
@@ -89,6 +100,7 @@ const imeKorisnika = user?.first_name || "Serviser"; \ No newline at end of file diff --git a/001.FRONTEND/src/pages/operativa/radni-nalozi/novi.astro b/001.FRONTEND/src/pages/operativa/radni-nalozi/novi.astro index 0214057..c3f56cd 100644 --- a/001.FRONTEND/src/pages/operativa/radni-nalozi/novi.astro +++ b/001.FRONTEND/src/pages/operativa/radni-nalozi/novi.astro @@ -127,7 +127,7 @@ const { strojevi } = await fetchStrojeviData(); const realInput = document.getElementById('real-stroj-input') as HTMLInputElement; const strojOptions = document.querySelectorAll('.stroj-opt'); - // Funkcija za inicijalizaciju broja naloga na klijentu + // Inicijalizacija broja naloga na klijentu async function initBrojNaloga() { const displayElement = document.getElementById('broj-text'); if (displayElement) { @@ -136,7 +136,6 @@ const { strojevi } = await fetchStrojeviData(); } } - // Pozivamo funkciju čim se skripta učita initBrojNaloga(); // 1. DROPDOWN LOGIKA @@ -175,35 +174,55 @@ const { strojevi } = await fetchStrojeviData(); // 3. SLANJE NA API form.addEventListener('submit', async (e) => { e.preventDefault(); - const btn = document.getElementById('submit-btn'); - if (!btn) return; + const gumb = document.getElementById('submit-btn') as HTMLButtonElement; + if (!gumb) return; - btn.setAttribute('disabled', 'true'); - const originalContent = btn.innerHTML; - btn.innerHTML = ' Sinkronizacija...'; + gumb.setAttribute('disabled', 'true'); + const originalContent = gumb.innerHTML; + gumb.innerHTML = ' Sinkronizacija...'; const formData = new FormData(form); - // Brišemo broj_naloga kako bi backend aktivirao save() logiku + // Brišemo broj_naloga kako bi backend sam aktivirao save() sekvencu formData.delete('broj_naloga'); - // Dodavanje izvrsitelja iz localStorage const userString = localStorage.getItem('user_info'); if (userString) { const user = JSON.parse(userString); formData.append('izvrsitelj', user.id); } - const nalog = await createNalog(formData); + try { + const nalog = await createNalog(formData); - if (nalog && nalog.id) { - window.location.href = `/operativa/radni-nalozi/${nalog.id}`; - } else { - btn.removeAttribute('disabled'); - btn.innerHTML = originalContent; + if (nalog && nalog.id) { + // Budući da views.py sada radi refresh_from_db(), nalog.broj_naloga je ovdje 100% čisti 'RN-2026-XXXX' + sessionStorage.setItem( + 'pending_toast', + JSON.stringify({ + type: 'success', + message: `Radni nalog ${nalog.broj_naloga} uspješno kreiran!` + }) + ); + + window.location.href = `/operativa/radni-nalozi/${nalog.id}`; + } else { + ponistiLoading(gumb, originalContent); + } + } catch (err) { + console.error("Kritičan krah pri izradi naloga:", err); + if (typeof window !== 'undefined') { + window.showToast?.("Komunikacija s poslužiteljem nije uspjela.", "error"); + } + ponistiLoading(gumb, originalContent); } }); + function ponistiLoading(gumb: HTMLElement, originalContent: string) { + gumb.removeAttribute('disabled'); + gumb.innerHTML = originalContent; + } + document.addEventListener('click', (e) => { if (trigger && optionsList && !trigger.contains(e.target as Node) && !optionsList.contains(e.target as Node)) { optionsList.classList.add('hidden');