From 08b9359a761c84b0b0c4dcc62095455ca28cc6c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dalibor=20Markovi=C4=87?= Date: Thu, 4 Jun 2026 02:50:48 +0200 Subject: [PATCH] Embed implementacija, animacije, sidebar hover --- assets.go | 12 +++++++ cmd/ntech/main.go | 15 +++++--- internal/config/setup.go | 6 ++-- internal/db/sqlite/migracije.go | 16 ++++----- internal/handler/handler.go | 8 +++-- internal/handler/kes.go | 9 ++--- web/static/css/main.css | 35 ++++++++++++++++++- web/templates/stranice/admin_korisnici.html | 17 ++------- .../stranice/admin_login_istorija.html | 13 +++++-- web/templates/stranice/admin_profil.html | 13 +++++-- web/templates/stranice/dashboard.html | 29 +++++---------- web/templates/stranice/dobavljac_forma.html | 19 +--------- web/templates/stranice/dobavljaci.html | 21 ++--------- web/templates/stranice/izvestaji.html | 23 +++--------- web/templates/stranice/kategorije.html | 15 ++------ web/templates/stranice/klijent_forma.html | 19 +--------- web/templates/stranice/klijenti.html | 21 ++--------- web/templates/stranice/magacin.html | 13 ++----- web/templates/stranice/magacin_forma.html | 2 +- web/templates/stranice/nabavka_detalji.html | 19 ++-------- web/templates/stranice/nabavka_forma.html | 22 ++---------- web/templates/stranice/nabavke.html | 21 ++--------- web/templates/stranice/podesavanja.html | 17 ++++++--- web/templates/stranice/podsetnici.html | 21 ++--------- web/templates/stranice/podsetnik_forma.html | 19 +--------- web/templates/stranice/prijava.html | 2 +- web/templates/stranice/prodaja.html | 21 ++--------- web/templates/stranice/prodaja_detalji.html | 17 ++------- web/templates/stranice/prodaja_forma.html | 18 ++-------- web/templates/stranice/servis.html | 21 ++--------- web/templates/stranice/servis_detalji.html | 25 +++---------- web/templates/stranice/servis_forma.html | 16 +-------- web/templates/stranice/setup.html | 2 +- web/templates/stranice/totp_provera.html | 2 +- 34 files changed, 171 insertions(+), 378 deletions(-) create mode 100644 assets.go diff --git a/assets.go b/assets.go new file mode 100644 index 0000000..2927ada --- /dev/null +++ b/assets.go @@ -0,0 +1,12 @@ +package assets + +import "embed" + +//go:embed migrations +var MigracijeFS embed.FS + +//go:embed web/static/css +var StaticFS embed.FS + +//go:embed web/templates +var TemplatesFS embed.FS diff --git a/cmd/ntech/main.go b/cmd/ntech/main.go index 1771678..5ff6896 100644 --- a/cmd/ntech/main.go +++ b/cmd/ntech/main.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "io/fs" "log" "net/http" "os" @@ -11,6 +12,7 @@ import ( "sort" "time" + "ntech" "ntech/internal/auth" "ntech/internal/config" "ntech/internal/db/sqlite" @@ -30,7 +32,7 @@ func main() { auth.InitAuthLog() if config.JelPrvoPokretanje() { - config.PokreniSetup() + config.PokreniSetup(assets.TemplatesFS) return } @@ -50,7 +52,7 @@ func main() { } defer db.Close() - if err := sqlite.PokreniMigracije(db, "migrations"); err != nil { + if err := sqlite.PokreniMigracije(db, assets.MigracijeFS); err != nil { log.Fatalf("Greška pri migracijama: %v", err) } log.Println("Migracije uspešno izvršene") @@ -75,7 +77,7 @@ func main() { h.Verzija = Verzija if os.Getenv("NTECH_ENV") == "production" { - kes, err := handler.KreirajKes() + kes, err := handler.KreirajKes(assets.TemplatesFS) if err != nil { log.Fatalf("Greška pri kreiranju keša šablona: %v", err) } @@ -88,8 +90,11 @@ func main() { r.Use(ntechmw.CsrfMiddleware) r.Use(middleware.Compress(5)) - // statični fajlovi - r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir("web/static")))) + // uploads se servira sa diska (runtime fajlovi) + r.Handle("/static/uploads/*", http.StripPrefix("/static/uploads/", http.FileServer(http.Dir("web/static/uploads")))) + // ostali statični fajlovi (CSS) iz embed FS-a + staticSub, _ := fs.Sub(assets.StaticFS, "web/static") + r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.FS(staticSub)))) // javne rute (bez autentifikacije) r.Get("/prijava", h.PrikazPrijave) diff --git a/internal/config/setup.go b/internal/config/setup.go index 0a3232e..f1e281d 100644 --- a/internal/config/setup.go +++ b/internal/config/setup.go @@ -3,9 +3,9 @@ package config import ( "encoding/json" "fmt" + "io/fs" "log" "net/http" - "os" "strings" "github.com/go-chi/chi/v5" @@ -13,7 +13,7 @@ import ( ) // PokreniSetup pokreće WebView prozor za prvo podešavanje -func PokreniSetup() { +func PokreniSetup(fsys fs.FS) { port := NadjiSlobodanPort() if port == 0 { log.Fatal("Greška: nije pronađen nijedan slobodan port") @@ -30,7 +30,7 @@ func PokreniSetup() { http.Error(w, "Greška", http.StatusInternalServerError) return } - sadrzaj, err := os.ReadFile("web/templates/setup/index.html") + sadrzaj, err := fs.ReadFile(fsys, "web/templates/setup/index.html") if err != nil { http.Error(w, "Greška", http.StatusInternalServerError) return diff --git a/internal/db/sqlite/migracije.go b/internal/db/sqlite/migracije.go index 4532f02..e7b352f 100644 --- a/internal/db/sqlite/migracije.go +++ b/internal/db/sqlite/migracije.go @@ -3,8 +3,8 @@ package sqlite import ( "database/sql" "fmt" - "os" - "path/filepath" + "io/fs" + "path" "sort" _ "modernc.org/sqlite" @@ -32,8 +32,8 @@ func OtvoriDB(putanja string) (*sql.DB, error) { return db, nil } -// PokreniMigracije izvršava sve SQL fajlove iz foldera koji još nisu izvršeni -func PokreniMigracije(db *sql.DB, folder string) error { +// PokreniMigracije izvršava sve SQL fajlove iz fs.FS koji još nisu izvršeni +func PokreniMigracije(db *sql.DB, fsys fs.FS) error { // kreiramo tabelu za praćenje migracija ako ne postoji _, err := db.Exec(` CREATE TABLE IF NOT EXISTS migracije ( @@ -46,7 +46,7 @@ func PokreniMigracije(db *sql.DB, folder string) error { } // čitamo sve .sql fajlove iz foldera - unosi, err := os.ReadDir(folder) + unosi, err := fs.ReadDir(fsys, "migrations") if err != nil { return fmt.Errorf("ntech: PokreniMigracije: čitanje foldera: %w", err) } @@ -57,7 +57,7 @@ func PokreniMigracije(db *sql.DB, folder string) error { }) for _, unos := range unosi { - if filepath.Ext(unos.Name()) != ".sql" { + if path.Ext(unos.Name()) != ".sql" { continue } @@ -74,8 +74,8 @@ func PokreniMigracije(db *sql.DB, folder string) error { } // čitamo sadržaj SQL fajla - putanja := filepath.Join(folder, naziv) - sadrzaj, err := os.ReadFile(putanja) + putanja := "migrations/" + naziv + sadrzaj, err := fs.ReadFile(fsys, putanja) if err != nil { return fmt.Errorf("ntech: PokreniMigracije: čitanje %s: %w", naziv, err) } diff --git a/internal/handler/handler.go b/internal/handler/handler.go index e874056..bacbb46 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -3,12 +3,13 @@ package handler import ( "database/sql" "html/template" + "io/fs" + "net/http" "ntech/internal/db" "ntech/internal/db/sqlite" "ntech/internal/middleware" "ntech/internal/model" - "net/http" ) // Handler drži zavisnosti koje su potrebne svim handlerima @@ -26,8 +27,9 @@ type Handler struct { PodsetniciFRepo db.PodsetnikRepository PokusajiRepo db.PokusajiPrijaveRepository LoginIstorijsaRepo db.LoginIstorijsaRepository - Verzija string - Templates map[string]*template.Template + Verzija string + Templates map[string]*template.Template + TemplatesFS fs.FS } // Novi kreira novi Handler sa datom bazom diff --git a/internal/handler/kes.go b/internal/handler/kes.go index bedd579..ea52ee1 100644 --- a/internal/handler/kes.go +++ b/internal/handler/kes.go @@ -3,6 +3,7 @@ package handler import ( "fmt" "html/template" + "io/fs" "log" "net/http" ) @@ -34,15 +35,15 @@ var standaloneIme = []string{ "prijava", "setup", "totp_provera", "prodaja_stampa", } -// KreirajKes parsuje sve šablone i vraća ih keširane u mapi -func KreirajKes() (map[string]*template.Template, error) { +// KreirajKes parsuje sve šablone iz fsys i vraća ih keširane u mapi +func KreirajKes(fsys fs.FS) (map[string]*template.Template, error) { kes := make(map[string]*template.Template) for _, ime := range saSidebar { fajlovi := make([]string, len(bazniSabloni), len(bazniSabloni)+1) copy(fajlovi, bazniSabloni) fajlovi = append(fajlovi, "web/templates/stranice/"+ime+".html") - t, err := template.ParseFiles(fajlovi...) + t, err := template.ParseFS(fsys, fajlovi...) if err != nil { return nil, fmt.Errorf("kes: %s: %w", ime, err) } @@ -50,7 +51,7 @@ func KreirajKes() (map[string]*template.Template, error) { } for _, ime := range standaloneIme { - t, err := template.ParseFiles("web/templates/stranice/" + ime + ".html") + t, err := template.ParseFS(fsys, "web/templates/stranice/"+ime+".html") if err != nil { return nil, fmt.Errorf("kes: %s: %w", ime, err) } diff --git a/web/static/css/main.css b/web/static/css/main.css index 4735b29..f8f302f 100644 --- a/web/static/css/main.css +++ b/web/static/css/main.css @@ -31,6 +31,7 @@ body { .sidebar.skupljen { width: 60px; + overflow: hidden; } /* vrh sidebara — logo zona */ @@ -131,7 +132,7 @@ body { white-space: nowrap; position: relative; text-decoration: none; - transition: background 0.2s, color 0.2s; + transition: background 0.2s, color 0.2s, transform 0.15s ease; } .nav-stavka:hover { @@ -139,6 +140,14 @@ body { color: var(--tekst-jak); } +.sidebar:not(.skupljen) .nav-stavka:hover { + transform: scale(1.03); +} + +.sidebar.skupljen .nav-stavka:hover svg { + transform: scale(1.15); +} + .nav-stavka.aktivan { background: var(--sb-aktivan); color: var(--tekst-jak); @@ -159,6 +168,7 @@ body { flex-shrink: 0; width: 20px; height: 20px; + transition: transform 0.15s ease; } .nav-stavka span { @@ -437,6 +447,29 @@ select { } } +/* animacije */ +@keyframes fadeInUp { + from { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes slideDown { + from { opacity: 0; transform: translateY(-10px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes shake { + 0%, 100% { transform: translateX(0); } + 20% { transform: translateX(-6px); } + 40% { transform: translateX(6px); } + 60% { transform: translateX(-4px); } + 80% { transform: translateX(4px); } +} + +.animiraj { + animation: fadeInUp 0.4s ease both; +} + /* gornja traka magacina — responsive */ .magacin-traka { display: flex; diff --git a/web/templates/stranice/admin_korisnici.html b/web/templates/stranice/admin_korisnici.html index c5e1807..a7e708e 100644 --- a/web/templates/stranice/admin_korisnici.html +++ b/web/templates/stranice/admin_korisnici.html @@ -8,17 +8,8 @@ from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } } - @keyframes fadeInUp { - from { opacity: 0; transform: translateY(8px); } - to { opacity: 1; transform: translateY(0); } - } - .poruka-animacija { animation: slideDown 0.3s ease forwards; } - .korisnici-tabela tbody tr { - animation: fadeInUp 0.25s ease forwards; - opacity: 0; - } .korisnici-tabela tbody tr:nth-child(1) { animation-delay: 0.04s; } .korisnici-tabela tbody tr:nth-child(2) { animation-delay: 0.08s; } .korisnici-tabela tbody tr:nth-child(3) { animation-delay: 0.12s; } @@ -31,8 +22,6 @@ .korisnici-tabela tbody tr:nth-child(10) { animation-delay: 0.40s; } .nova-forma-kartica { - animation: fadeInUp 0.25s ease forwards; - opacity: 0; animation-delay: 0.30s; } @@ -52,7 +41,7 @@ {{end}} -
+
Korisnici sistema
@@ -70,7 +59,7 @@ {{range .Korisnici}} - + {{.KorisnickoIme}} {{if eq $.KorisnikUloga "superadmin"}} @@ -127,7 +116,7 @@
-
+
Novi korisnik
diff --git a/web/templates/stranice/admin_login_istorija.html b/web/templates/stranice/admin_login_istorija.html index c5e0d54..a3e45d7 100644 --- a/web/templates/stranice/admin_login_istorija.html +++ b/web/templates/stranice/admin_login_istorija.html @@ -2,6 +2,15 @@ {{define "naslov"}}Istorija prijava — NTech{{end}} +{{define "dodatni-css"}} + +{{end}} + {{define "sadrzaj"}}
@@ -15,7 +24,7 @@
-
+
Poslednjih 50 pokušaja Vreme prikazano u UTC @@ -35,7 +44,7 @@ {{range .Istorija}} - + {{.Vreme.Format "02.01.2006. 15:04:05"}} diff --git a/web/templates/stranice/admin_profil.html b/web/templates/stranice/admin_profil.html index 8f8cba0..5a04ebe 100644 --- a/web/templates/stranice/admin_profil.html +++ b/web/templates/stranice/admin_profil.html @@ -2,7 +2,14 @@ {{define "naslov"}}Moj profil — NTech{{end}} -{{define "dodatni-css"}}{{end}} +{{define "dodatni-css"}} + +{{end}} {{define "sadrzaj"}}
@@ -16,7 +23,7 @@ {{end}} -
+
Promena lozinke
@@ -52,7 +59,7 @@
-
+
Dvostepena verifikacija (2FA)
diff --git a/web/templates/stranice/dashboard.html b/web/templates/stranice/dashboard.html index 9d535b6..fa7ef78 100644 --- a/web/templates/stranice/dashboard.html +++ b/web/templates/stranice/dashboard.html @@ -4,25 +4,12 @@ {{define "dodatni-css"}} @@ -109,7 +96,7 @@
-
+
Mesečni prihod — poslednjih 12 meseci
@@ -156,7 +143,7 @@
-
+
Stari otvoreni nalozi — bez završetka duže od 14 dana
{{if .StariNalozi}}
@@ -198,10 +185,10 @@ {{end}}
-
+
-
+
Najprodavaniji artikli — top 10
{{if .TopArtikli}} @@ -235,7 +222,7 @@ -
+
Najvažniji klijenti — top 10
{{if .TopKlijenti}}
diff --git a/web/templates/stranice/kategorije.html b/web/templates/stranice/kategorije.html index 9676c3d..2f2acb9 100644 --- a/web/templates/stranice/kategorije.html +++ b/web/templates/stranice/kategorije.html @@ -8,23 +8,12 @@ from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } } - @keyframes fadeInUp { - from { opacity: 0; transform: translateY(8px); } - to { opacity: 1; transform: translateY(0); } - } - .poruka-animacija { animation: slideDown 0.3s ease forwards; } .kat-forma-kartica { - animation: fadeInUp 0.25s ease forwards; - opacity: 0; animation-delay: 0.04s; } - .kat-red { - animation: fadeInUp 0.25s ease forwards; - opacity: 0; - } .kat-red:nth-child(1) { animation-delay: 0.12s; } .kat-red:nth-child(2) { animation-delay: 0.18s; } .kat-red:nth-child(3) { animation-delay: 0.24s; } @@ -53,7 +42,7 @@ -
+
Nova kategorija
@@ -87,7 +76,7 @@ Postojeće kategorije
{{range .Kategorije}} -
+
{{.Naziv}}
{{if .Opis}} diff --git a/web/templates/stranice/klijent_forma.html b/web/templates/stranice/klijent_forma.html index 8eebd86..45c273c 100644 --- a/web/templates/stranice/klijent_forma.html +++ b/web/templates/stranice/klijent_forma.html @@ -4,23 +4,6 @@ {{define "dodatni-css"}} +{{end}} + {{define "sadrzaj"}}
@@ -13,7 +22,7 @@
-
+
Logo firme @@ -45,7 +54,7 @@ -
+
Firma @@ -110,7 +119,7 @@
-
+
Izgled @@ -134,7 +143,7 @@
-
+
Sistem diff --git a/web/templates/stranice/podsetnici.html b/web/templates/stranice/podsetnici.html index 32bc153..ec219a1 100644 --- a/web/templates/stranice/podsetnici.html +++ b/web/templates/stranice/podsetnici.html @@ -9,20 +9,10 @@ to { opacity: 1; transform: translateY(0); } } - @keyframes fadeInUp { - from { opacity: 0; transform: translateY(8px); } - to { opacity: 1; transform: translateY(0); } - } - .poruka-animacija { animation: slideDown 0.3s ease forwards; } - .pod-tabela tbody tr { - animation: fadeInUp 0.25s ease forwards; - opacity: 0; - } - .pod-tabela tbody tr:nth-child(1) { animation-delay: 0.04s; } .pod-tabela tbody tr:nth-child(2) { animation-delay: 0.08s; } .pod-tabela tbody tr:nth-child(3) { animation-delay: 0.12s; } @@ -40,11 +30,6 @@ gap: 12px; } - .pod-kartica { - animation: fadeInUp 0.25s ease forwards; - opacity: 0; - } - .pod-kartica:nth-child(1) { animation-delay: 0.04s; } .pod-kartica:nth-child(2) { animation-delay: 0.10s; } .pod-kartica:nth-child(3) { animation-delay: 0.16s; } @@ -90,7 +75,7 @@
-
+
@@ -103,7 +88,7 @@ {{range .Podsetnici}} - @@ -165,7 +150,7 @@
{{range .Podsetnici}} -
+
{{.Naslov}}
diff --git a/web/templates/stranice/podsetnik_forma.html b/web/templates/stranice/podsetnik_forma.html index e4e4d65..0e8b9b8 100644 --- a/web/templates/stranice/podsetnik_forma.html +++ b/web/templates/stranice/podsetnik_forma.html @@ -4,23 +4,6 @@ {{define "dodatni-css"}} -
+
-
+
@@ -107,7 +92,7 @@ {{range .Nalozi}} -
@@ -149,7 +134,7 @@
{{range .Nalozi}} -
+
{{.BrojNaloga}}
diff --git a/web/templates/stranice/prodaja_detalji.html b/web/templates/stranice/prodaja_detalji.html index f3ad990..c2f2968 100644 --- a/web/templates/stranice/prodaja_detalji.html +++ b/web/templates/stranice/prodaja_detalji.html @@ -4,17 +4,6 @@ {{define "dodatni-css"}} -
+