Compare commits
4 Commits
8b949e1fb9
...
85cb1e25c7
| Author | SHA1 | Date | |
|---|---|---|---|
|
85cb1e25c7
|
|||
|
3c5c8060c1
|
|||
|
330f30d8bb
|
|||
|
c934dde4c0
|
@@ -253,6 +253,7 @@ func main() {
|
|||||||
r.With(doz("podesavanja.izmeni")).Post("/podesavanja/pdv-stope/{id}/aktivnost", h.PromeniAktivnostPdvStope)
|
r.With(doz("podesavanja.izmeni")).Post("/podesavanja/pdv-stope/{id}/aktivnost", h.PromeniAktivnostPdvStope)
|
||||||
r.With(doz("podesavanja.izmeni")).Post("/podesavanja/sacuvaj", h.SacuvajPodesavanja)
|
r.With(doz("podesavanja.izmeni")).Post("/podesavanja/sacuvaj", h.SacuvajPodesavanja)
|
||||||
r.With(doz("podesavanja.izmeni")).Post("/podesavanja/logo", h.OtpremiLogo)
|
r.With(doz("podesavanja.izmeni")).Post("/podesavanja/logo", h.OtpremiLogo)
|
||||||
|
r.With(doz("podesavanja.izmeni")).Post("/podesavanja/logo/ukloni", h.UkloniLogo)
|
||||||
r.With(doz("podesavanja.login_pozadina")).Post("/podesavanja/login-pozadina", h.OtpremiLoginPozadinu)
|
r.With(doz("podesavanja.login_pozadina")).Post("/podesavanja/login-pozadina", h.OtpremiLoginPozadinu)
|
||||||
r.With(doz("podesavanja.login_pozadina")).Post("/podesavanja/login-pozadina/ukloni", h.UkloniLoginPozadinu)
|
r.With(doz("podesavanja.login_pozadina")).Post("/podesavanja/login-pozadina/ukloni", h.UkloniLoginPozadinu)
|
||||||
r.With(doz("podesavanja.login_pozadina")).Post("/podesavanja/login-pozadina/stilovi", h.SacuvajLoginPozadinaStilove)
|
r.With(doz("podesavanja.login_pozadina")).Post("/podesavanja/login-pozadina/stilovi", h.SacuvajLoginPozadinaStilove)
|
||||||
@@ -359,6 +360,8 @@ func main() {
|
|||||||
r.With(doz("tema.lokalno")).Post("/profil/pozadina", h.ProfilOtpremiPozadinu)
|
r.With(doz("tema.lokalno")).Post("/profil/pozadina", h.ProfilOtpremiPozadinu)
|
||||||
r.With(doz("tema.lokalno")).Post("/profil/pozadina/ukloni", h.ProfilUkloniPozadinu)
|
r.With(doz("tema.lokalno")).Post("/profil/pozadina/ukloni", h.ProfilUkloniPozadinu)
|
||||||
r.With(doz("tema.lokalno")).Post("/profil/pozadina/stilovi", h.ProfilSacuvajPozadinuStilove)
|
r.With(doz("tema.lokalno")).Post("/profil/pozadina/stilovi", h.ProfilSacuvajPozadinuStilove)
|
||||||
|
r.Post("/profil/avatar", h.ProfilOtpremiAvatar)
|
||||||
|
r.Post("/profil/avatar/ukloni", h.ProfilUkloniAvatar)
|
||||||
})
|
})
|
||||||
|
|
||||||
slog.Info("NTech pokrenut", "port", port)
|
slog.Info("NTech pokrenut", "port", port)
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ type KorisniciRepository interface {
|
|||||||
SacuvajTotpTajnu(ctx context.Context, id int64, tajna string) error
|
SacuvajTotpTajnu(ctx context.Context, id int64, tajna string) error
|
||||||
SacuvajLokalnuTemu(ctx context.Context, id int64, lokalnaTema string, koristi bool) error
|
SacuvajLokalnuTemu(ctx context.Context, id int64, lokalnaTema string, koristi bool) error
|
||||||
SacuvajLokalnuPozadinu(ctx context.Context, id int64, pozadina, opacity, blur, blurPozadine, glassOpacity string) error
|
SacuvajLokalnuPozadinu(ctx context.Context, id int64, pozadina, opacity, blur, blurPozadine, glassOpacity string) error
|
||||||
|
SacuvajAvatar(ctx context.Context, id int64, putanja string) error
|
||||||
PostojiIjedan(ctx context.Context) (bool, error)
|
PostojiIjedan(ctx context.Context) (bool, error)
|
||||||
Obrisi(ctx context.Context, id int64) error
|
Obrisi(ctx context.Context, id int64) error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,8 +34,9 @@ func (r *ArtikalRepo) Lista(ctx context.Context, filter db.ArtikalFilter) ([]mod
|
|||||||
args := []any{}
|
args := []any{}
|
||||||
|
|
||||||
if filter.Pretraga != "" {
|
if filter.Pretraga != "" {
|
||||||
upit += " AND a.naziv LIKE ?"
|
upit += " AND (a.naziv LIKE ? OR a.lokacija LIKE ? OR k.naziv LIKE ?)"
|
||||||
args = append(args, "%"+filter.Pretraga+"%")
|
t := "%" + filter.Pretraga + "%"
|
||||||
|
args = append(args, t, t, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
if filter.KategorijaID != nil {
|
if filter.KategorijaID != nil {
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ func (r *sqliteIzvestajRepo) PoslednjiServisi(ctx context.Context, limit int) ([
|
|||||||
|
|
||||||
func (r *sqliteIzvestajRepo) KriticneZalihe(ctx context.Context, limit int) ([]model.ZalihaRed, error) {
|
func (r *sqliteIzvestajRepo) KriticneZalihe(ctx context.Context, limit int) ([]model.ZalihaRed, error) {
|
||||||
rows, err := r.db.QueryContext(ctx, `
|
rows, err := r.db.QueryContext(ctx, `
|
||||||
SELECT naziv, kolicina FROM artikli
|
SELECT naziv, kolicina, kolicina_min FROM artikli
|
||||||
WHERE kolicina <= kolicina_min
|
WHERE kolicina <= kolicina_min
|
||||||
ORDER BY kolicina ASC LIMIT ?`, limit)
|
ORDER BY kolicina ASC LIMIT ?`, limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -85,7 +85,7 @@ func (r *sqliteIzvestajRepo) KriticneZalihe(ctx context.Context, limit int) ([]m
|
|||||||
var lista []model.ZalihaRed
|
var lista []model.ZalihaRed
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var z model.ZalihaRed
|
var z model.ZalihaRed
|
||||||
if err := rows.Scan(&z.Naziv, &z.Kolicina); err != nil {
|
if err := rows.Scan(&z.Naziv, &z.Kolicina, &z.KolicinaMin); err != nil {
|
||||||
return nil, fmt.Errorf("ntech: izvestaj.KriticneZalihe: %w", err)
|
return nil, fmt.Errorf("ntech: izvestaj.KriticneZalihe: %w", err)
|
||||||
}
|
}
|
||||||
lista = append(lista, z)
|
lista = append(lista, z)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ type sqliteKorisniciRepo struct {
|
|||||||
// NULL vrednosti — deljeno između skeniraiKorisnika (jedan red) i Lista (više redova)
|
// NULL vrednosti — deljeno između skeniraiKorisnika (jedan red) i Lista (više redova)
|
||||||
func dodeliOpcijeKorisnika(k *model.Korisnik, aktivan, koristiLokalnuTemu int,
|
func dodeliOpcijeKorisnika(k *model.Korisnik, aktivan, koristiLokalnuTemu int,
|
||||||
lokalnaTema, lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur,
|
lokalnaTema, lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur,
|
||||||
lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity sql.NullString,
|
lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity, avatarPutanja sql.NullString,
|
||||||
datumKreiranja time.Time) {
|
datumKreiranja time.Time) {
|
||||||
k.Aktivan = aktivan == 1
|
k.Aktivan = aktivan == 1
|
||||||
k.LokalnaTema = lokalnaTema.String
|
k.LokalnaTema = lokalnaTema.String
|
||||||
@@ -32,6 +32,7 @@ func dodeliOpcijeKorisnika(k *model.Korisnik, aktivan, koristiLokalnuTemu int,
|
|||||||
k.LokalnaPozadinaBlur = lokalnaPozadinaBlur.String
|
k.LokalnaPozadinaBlur = lokalnaPozadinaBlur.String
|
||||||
k.LokalnaPozadinaBlurPozadine = lokalnaPozadinaBlurPozadine.String
|
k.LokalnaPozadinaBlurPozadine = lokalnaPozadinaBlurPozadine.String
|
||||||
k.LokalnaPozadinaGlassOpacity = lokalnaPozadinaGlassOpacity.String
|
k.LokalnaPozadinaGlassOpacity = lokalnaPozadinaGlassOpacity.String
|
||||||
|
k.AvatarPutanja = avatarPutanja.String
|
||||||
}
|
}
|
||||||
|
|
||||||
// skeniraiKorisnika čita jedan red iz baze i popunjava model.Korisnik
|
// skeniraiKorisnika čita jedan red iz baze i popunjava model.Korisnik
|
||||||
@@ -40,18 +41,19 @@ func skeniraiKorisnika(row interface{ Scan(...any) error }) (*model.Korisnik, er
|
|||||||
var aktivan, koristiLokalnuTemu int
|
var aktivan, koristiLokalnuTemu int
|
||||||
var lokalnaTema sql.NullString
|
var lokalnaTema sql.NullString
|
||||||
var lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur, lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity sql.NullString
|
var lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur, lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity sql.NullString
|
||||||
|
var avatarPutanja sql.NullString
|
||||||
var datumKreiranja time.Time
|
var datumKreiranja time.Time
|
||||||
if err := row.Scan(
|
if err := row.Scan(
|
||||||
&k.ID, &k.KorisnickoIme, &k.LozinkaHash, &k.Uloga, &aktivan, &k.TotpTajna,
|
&k.ID, &k.KorisnickoIme, &k.LozinkaHash, &k.Uloga, &aktivan, &k.TotpTajna,
|
||||||
&lokalnaTema, &koristiLokalnuTemu, &datumKreiranja,
|
&lokalnaTema, &koristiLokalnuTemu, &datumKreiranja,
|
||||||
&lokalnaPozadina, &lokalnaPozadinaOpacity, &lokalnaPozadinaBlur,
|
&lokalnaPozadina, &lokalnaPozadinaOpacity, &lokalnaPozadinaBlur,
|
||||||
&lokalnaPozadinaBlurPozadine, &lokalnaPozadinaGlassOpacity,
|
&lokalnaPozadinaBlurPozadine, &lokalnaPozadinaGlassOpacity, &avatarPutanja,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
dodeliOpcijeKorisnika(k, aktivan, koristiLokalnuTemu, lokalnaTema,
|
dodeliOpcijeKorisnika(k, aktivan, koristiLokalnuTemu, lokalnaTema,
|
||||||
lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur,
|
lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur,
|
||||||
lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity, datumKreiranja)
|
lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity, avatarPutanja, datumKreiranja)
|
||||||
return k, nil
|
return k, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +93,7 @@ func (r *sqliteKorisniciRepo) DohvatiPoImenu(ctx context.Context, korisnickoIme
|
|||||||
COALESCE(lokalna_tema, ''), koristi_lokalnu_temu, datum_kreiranja,
|
COALESCE(lokalna_tema, ''), koristi_lokalnu_temu, datum_kreiranja,
|
||||||
COALESCE(lokalna_pozadina, ''), COALESCE(lokalna_pozadina_opacity, '50'),
|
COALESCE(lokalna_pozadina, ''), COALESCE(lokalna_pozadina_opacity, '50'),
|
||||||
COALESCE(lokalna_pozadina_blur, '12'), COALESCE(lokalna_pozadina_blur_pozadine, '0'),
|
COALESCE(lokalna_pozadina_blur, '12'), COALESCE(lokalna_pozadina_blur_pozadine, '0'),
|
||||||
COALESCE(lokalna_pozadina_glass_opacity, '10')
|
COALESCE(lokalna_pozadina_glass_opacity, '10'), COALESCE(avatar_putanja, '')
|
||||||
FROM korisnici WHERE korisnicko_ime = ?`, korisnickoIme)
|
FROM korisnici WHERE korisnicko_ime = ?`, korisnickoIme)
|
||||||
k, err := skeniraiKorisnika(row)
|
k, err := skeniraiKorisnika(row)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -107,7 +109,7 @@ func (r *sqliteKorisniciRepo) DohvatiPoID(ctx context.Context, id int64) (*model
|
|||||||
COALESCE(lokalna_tema, ''), koristi_lokalnu_temu, datum_kreiranja,
|
COALESCE(lokalna_tema, ''), koristi_lokalnu_temu, datum_kreiranja,
|
||||||
COALESCE(lokalna_pozadina, ''), COALESCE(lokalna_pozadina_opacity, '50'),
|
COALESCE(lokalna_pozadina, ''), COALESCE(lokalna_pozadina_opacity, '50'),
|
||||||
COALESCE(lokalna_pozadina_blur, '12'), COALESCE(lokalna_pozadina_blur_pozadine, '0'),
|
COALESCE(lokalna_pozadina_blur, '12'), COALESCE(lokalna_pozadina_blur_pozadine, '0'),
|
||||||
COALESCE(lokalna_pozadina_glass_opacity, '10')
|
COALESCE(lokalna_pozadina_glass_opacity, '10'), COALESCE(avatar_putanja, '')
|
||||||
FROM korisnici WHERE id = ?`, id)
|
FROM korisnici WHERE id = ?`, id)
|
||||||
k, err := skeniraiKorisnika(row)
|
k, err := skeniraiKorisnika(row)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -123,7 +125,7 @@ func (r *sqliteKorisniciRepo) Lista(ctx context.Context) ([]model.Korisnik, erro
|
|||||||
COALESCE(lokalna_tema, ''), koristi_lokalnu_temu, datum_kreiranja,
|
COALESCE(lokalna_tema, ''), koristi_lokalnu_temu, datum_kreiranja,
|
||||||
COALESCE(lokalna_pozadina, ''), COALESCE(lokalna_pozadina_opacity, '50'),
|
COALESCE(lokalna_pozadina, ''), COALESCE(lokalna_pozadina_opacity, '50'),
|
||||||
COALESCE(lokalna_pozadina_blur, '12'), COALESCE(lokalna_pozadina_blur_pozadine, '0'),
|
COALESCE(lokalna_pozadina_blur, '12'), COALESCE(lokalna_pozadina_blur_pozadine, '0'),
|
||||||
COALESCE(lokalna_pozadina_glass_opacity, '10')
|
COALESCE(lokalna_pozadina_glass_opacity, '10'), COALESCE(avatar_putanja, '')
|
||||||
FROM korisnici ORDER BY datum_kreiranja ASC`)
|
FROM korisnici ORDER BY datum_kreiranja ASC`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("ntech: korisnici.Lista: %w", err)
|
return nil, fmt.Errorf("ntech: korisnici.Lista: %w", err)
|
||||||
@@ -135,22 +137,32 @@ func (r *sqliteKorisniciRepo) Lista(ctx context.Context) ([]model.Korisnik, erro
|
|||||||
var aktivan, koristiLokalnuTemu int
|
var aktivan, koristiLokalnuTemu int
|
||||||
var lokalnaTema sql.NullString
|
var lokalnaTema sql.NullString
|
||||||
var lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur, lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity sql.NullString
|
var lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur, lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity sql.NullString
|
||||||
|
var avatarPutanja sql.NullString
|
||||||
var datumKreiranja time.Time
|
var datumKreiranja time.Time
|
||||||
if err := rows.Scan(&k.ID, &k.KorisnickoIme, &k.Uloga, &aktivan, &k.TotpTajna,
|
if err := rows.Scan(&k.ID, &k.KorisnickoIme, &k.Uloga, &aktivan, &k.TotpTajna,
|
||||||
&lokalnaTema, &koristiLokalnuTemu, &datumKreiranja,
|
&lokalnaTema, &koristiLokalnuTemu, &datumKreiranja,
|
||||||
&lokalnaPozadina, &lokalnaPozadinaOpacity, &lokalnaPozadinaBlur, &lokalnaPozadinaBlurPozadine,
|
&lokalnaPozadina, &lokalnaPozadinaOpacity, &lokalnaPozadinaBlur, &lokalnaPozadinaBlurPozadine,
|
||||||
&lokalnaPozadinaGlassOpacity); err != nil {
|
&lokalnaPozadinaGlassOpacity, &avatarPutanja); err != nil {
|
||||||
return nil, fmt.Errorf("ntech: korisnici.Lista: %w", err)
|
return nil, fmt.Errorf("ntech: korisnici.Lista: %w", err)
|
||||||
}
|
}
|
||||||
dodeliOpcijeKorisnika(&k, aktivan, koristiLokalnuTemu, lokalnaTema,
|
dodeliOpcijeKorisnika(&k, aktivan, koristiLokalnuTemu, lokalnaTema,
|
||||||
lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur,
|
lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur,
|
||||||
lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity, datumKreiranja)
|
lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity, avatarPutanja, datumKreiranja)
|
||||||
r.desifrujTotpTajnu(&k)
|
r.desifrujTotpTajnu(&k)
|
||||||
lista = append(lista, k)
|
lista = append(lista, k)
|
||||||
}
|
}
|
||||||
return lista, nil
|
return lista, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *sqliteKorisniciRepo) SacuvajAvatar(ctx context.Context, id int64, putanja string) error {
|
||||||
|
_, err := r.db.ExecContext(ctx,
|
||||||
|
`UPDATE korisnici SET avatar_putanja = ? WHERE id = ?`, putanja, id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ntech: korisnici.SacuvajAvatar: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *sqliteKorisniciRepo) SacuvajLokalnuPozadinu(ctx context.Context, id int64, pozadina, opacity, blur, blurPozadine, glassOpacity string) error {
|
func (r *sqliteKorisniciRepo) SacuvajLokalnuPozadinu(ctx context.Context, id int64, pozadina, opacity, blur, blurPozadine, glassOpacity string) error {
|
||||||
_, err := r.db.ExecContext(ctx,
|
_, err := r.db.ExecContext(ctx,
|
||||||
`UPDATE korisnici SET lokalna_pozadina = ?, lokalna_pozadina_opacity = ?, lokalna_pozadina_blur = ?, lokalna_pozadina_blur_pozadine = ?, lokalna_pozadina_glass_opacity = ? WHERE id = ?`,
|
`UPDATE korisnici SET lokalna_pozadina = ?, lokalna_pozadina_opacity = ?, lokalna_pozadina_blur = ?, lokalna_pozadina_blur_pozadine = ?, lokalna_pozadina_glass_opacity = ? WHERE id = ?`,
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ func (h *Handler) Dashboard(w http.ResponseWriter, r *http.Request) {
|
|||||||
} else {
|
} else {
|
||||||
for _, z := range redovi {
|
for _, z := range redovi {
|
||||||
boja := "#f97316"
|
boja := "#f97316"
|
||||||
if z.Kolicina == 0 {
|
if z.Kolicina == 0 || (z.KolicinaMin > 0 && z.Kolicina <= z.KolicinaMin/2) {
|
||||||
boja = "#dc2626"
|
boja = "#dc2626"
|
||||||
}
|
}
|
||||||
kriticneZalihe = append(kriticneZalihe, model.StavkaZalihe{
|
kriticneZalihe = append(kriticneZalihe, model.StavkaZalihe{
|
||||||
|
|||||||
@@ -161,12 +161,13 @@ func (h *Handler) popuniPodaciStranice(r *http.Request, podesavanja map[string]s
|
|||||||
tema := "tamna"
|
tema := "tamna"
|
||||||
|
|
||||||
ps := model.PodaciStranice{
|
ps := model.PodaciStranice{
|
||||||
Tema: tema,
|
Tema: tema,
|
||||||
NazivFirme: podesavanja["naziv_firme"],
|
NazivFirme: podesavanja["naziv_firme"],
|
||||||
Podnazlov: podesavanja["podnazlov"],
|
Podnazlov: podesavanja["podnazlov"],
|
||||||
LogoTip: podesavanja["logo_tip"],
|
LogoPutanja: podesavanja["logo_putanja"],
|
||||||
LogoPutanja: podesavanja["logo_putanja"],
|
TopbarLogoSlika: podesavanja["topbar_logo_slika"] == "1",
|
||||||
Korisnik: "Admin",
|
TopbarLogoTekst: podesavanja["topbar_logo_tekst"] == "1",
|
||||||
|
Korisnik: "Admin",
|
||||||
}
|
}
|
||||||
var korisnik *model.Korisnik
|
var korisnik *model.Korisnik
|
||||||
if k := middleware.KorisnikIzKonteksta(r.Context()); k != nil {
|
if k := middleware.KorisnikIzKonteksta(r.Context()); k != nil {
|
||||||
@@ -174,6 +175,7 @@ func (h *Handler) popuniPodaciStranice(r *http.Request, podesavanja map[string]s
|
|||||||
ps.Korisnik = k.KorisnickoIme
|
ps.Korisnik = k.KorisnickoIme
|
||||||
ps.KorisnikIme = k.KorisnickoIme
|
ps.KorisnikIme = k.KorisnickoIme
|
||||||
ps.KorisnikUloga = k.Uloga
|
ps.KorisnikUloga = k.Uloga
|
||||||
|
ps.AvatarPutanja = k.AvatarPutanja
|
||||||
ps.Dozvole = h.DozvoleRepo.SveDozvole(r.Context(), k.Uloga)
|
ps.Dozvole = h.DozvoleRepo.SveDozvole(r.Context(), k.Uloga)
|
||||||
// lokalna tema korisnika — primenjuje se uvek kada je postavljena, bez obzira na KoristiLokalnuTemu
|
// lokalna tema korisnika — primenjuje se uvek kada je postavljena, bez obzira na KoristiLokalnuTemu
|
||||||
if k.LokalnaTema != "" {
|
if k.LokalnaTema != "" {
|
||||||
|
|||||||
@@ -28,8 +28,9 @@ type PodaciPodesavanja struct {
|
|||||||
Adresa string
|
Adresa string
|
||||||
Telefon string
|
Telefon string
|
||||||
PIB string
|
PIB string
|
||||||
LogoTip string
|
LogoPutanja string
|
||||||
LogoPutanja string
|
TopbarLogoSlika bool
|
||||||
|
TopbarLogoTekst bool
|
||||||
// profil firme — pravni/poreski status (Faza 0); određuje koji se zakonski moduli pale
|
// profil firme — pravni/poreski status (Faza 0); određuje koji se zakonski moduli pale
|
||||||
FirmaPravniOblik string
|
FirmaPravniOblik string
|
||||||
FirmaPdvObveznik string
|
FirmaPdvObveznik string
|
||||||
@@ -81,8 +82,9 @@ func (h *Handler) Podesavanja(w http.ResponseWriter, r *http.Request) {
|
|||||||
Adresa: podesavanja["adresa"],
|
Adresa: podesavanja["adresa"],
|
||||||
Telefon: podesavanja["telefon"],
|
Telefon: podesavanja["telefon"],
|
||||||
PIB: podesavanja["pib"],
|
PIB: podesavanja["pib"],
|
||||||
LogoTip: podesavanja["logo_tip"],
|
|
||||||
LogoPutanja: podesavanja["logo_putanja"],
|
LogoPutanja: podesavanja["logo_putanja"],
|
||||||
|
TopbarLogoSlika: podesavanja["topbar_logo_slika"] == "1",
|
||||||
|
TopbarLogoTekst: podesavanja["topbar_logo_tekst"] == "1",
|
||||||
Sacuvano: r.URL.Query().Get("sacuvano") == "1",
|
Sacuvano: r.URL.Query().Get("sacuvano") == "1",
|
||||||
BackupVracen: r.URL.Query().Get("sacuvano") == "vraceno",
|
BackupVracen: r.URL.Query().Get("sacuvano") == "vraceno",
|
||||||
Verzija: h.Verzija,
|
Verzija: h.Verzija,
|
||||||
@@ -233,13 +235,24 @@ func (h *Handler) SacuvajPodesavanja(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkbox-i: šalju vrednost samo kada su čekirani, pa ih uvek eksplicitno čitamo
|
||||||
|
topbarLogoSlika := "0"
|
||||||
|
if r.FormValue("topbar_logo_slika") == "1" {
|
||||||
|
topbarLogoSlika = "1"
|
||||||
|
}
|
||||||
|
topbarLogoTekst := "0"
|
||||||
|
if r.FormValue("topbar_logo_tekst") == "1" {
|
||||||
|
topbarLogoTekst = "1"
|
||||||
|
}
|
||||||
|
|
||||||
polja := map[string]string{
|
polja := map[string]string{
|
||||||
"naziv_firme": r.FormValue("naziv_firme"),
|
"naziv_firme": r.FormValue("naziv_firme"),
|
||||||
"podnazlov": r.FormValue("podnazlov"),
|
"podnazlov": r.FormValue("podnazlov"),
|
||||||
"adresa": r.FormValue("adresa"),
|
"adresa": r.FormValue("adresa"),
|
||||||
"telefon": r.FormValue("telefon"),
|
"telefon": r.FormValue("telefon"),
|
||||||
"pib": r.FormValue("pib"),
|
"pib": r.FormValue("pib"),
|
||||||
"logo_tip": r.FormValue("logo_tip"),
|
"topbar_logo_slika": topbarLogoSlika,
|
||||||
|
"topbar_logo_tekst": topbarLogoTekst,
|
||||||
// profil firme (Faza 0) — radio dugmad uvek šalju vrednost, pa se uredno čuvaju
|
// profil firme (Faza 0) — radio dugmad uvek šalju vrednost, pa se uredno čuvaju
|
||||||
"firma_pravni_oblik": r.FormValue("firma_pravni_oblik"),
|
"firma_pravni_oblik": r.FormValue("firma_pravni_oblik"),
|
||||||
"firma_pdv_obveznik": r.FormValue("firma_pdv_obveznik"),
|
"firma_pdv_obveznik": r.FormValue("firma_pdv_obveznik"),
|
||||||
@@ -412,6 +425,21 @@ func (h *Handler) OtpremiLogo(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Redirect(w, r, "/podesavanja?sacuvano=1", http.StatusSeeOther)
|
http.Redirect(w, r, "/podesavanja?sacuvano=1", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UkloniLogo briše logo fajl i čisti putanju iz podešavanja
|
||||||
|
func (h *Handler) UkloniLogo(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if _, ok := h.zahtevajDozvolu(w, r, "podesavanja.izmeni"); !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stari, _ := filepath.Glob("web/static/uploads/logo.*")
|
||||||
|
for _, s := range stari {
|
||||||
|
os.Remove(s)
|
||||||
|
}
|
||||||
|
if err := ntechsqlite.SacuvajPodesavanje(r.Context(), h.DB, "logo_putanja", ""); err != nil {
|
||||||
|
slog.Error("ukloni logo: greška pri čuvanju", "error", err)
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, "/admin/podesavanja/opste?sacuvano=1", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
// generisiImeUploada vraća slučajno hex ime (16 bajtova) sa datom ekstenzijom
|
// generisiImeUploada vraća slučajno hex ime (16 bajtova) sa datom ekstenzijom
|
||||||
func generisiImeUploada(ext string) (string, error) {
|
func generisiImeUploada(ext string) (string, error) {
|
||||||
buf := make([]byte, 16)
|
buf := make([]byte, 16)
|
||||||
@@ -624,8 +652,9 @@ func (h *Handler) napuniPodaciPodesavanja(r *http.Request, naslov string) (Podac
|
|||||||
Adresa: podesavanja["adresa"],
|
Adresa: podesavanja["adresa"],
|
||||||
Telefon: podesavanja["telefon"],
|
Telefon: podesavanja["telefon"],
|
||||||
PIB: podesavanja["pib"],
|
PIB: podesavanja["pib"],
|
||||||
LogoTip: podesavanja["logo_tip"],
|
|
||||||
LogoPutanja: podesavanja["logo_putanja"],
|
LogoPutanja: podesavanja["logo_putanja"],
|
||||||
|
TopbarLogoSlika: podesavanja["topbar_logo_slika"] == "1",
|
||||||
|
TopbarLogoTekst: podesavanja["topbar_logo_tekst"] == "1",
|
||||||
FirmaPravniOblik: vrednostIliDefault(podesavanja, "firma_pravni_oblik", "pausalac"),
|
FirmaPravniOblik: vrednostIliDefault(podesavanja, "firma_pravni_oblik", "pausalac"),
|
||||||
FirmaPdvObveznik: vrednostIliDefault(podesavanja, "firma_pdv_obveznik", "ne"),
|
FirmaPdvObveznik: vrednostIliDefault(podesavanja, "firma_pdv_obveznik", "ne"),
|
||||||
FirmaFiskalizacija: vrednostIliDefault(podesavanja, "firma_fiskalizacija", "ne"),
|
FirmaFiskalizacija: vrednostIliDefault(podesavanja, "firma_fiskalizacija", "ne"),
|
||||||
|
|||||||
@@ -289,3 +289,118 @@ func (h *Handler) SacuvajLokalnuTemu(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
http.Redirect(w, r, "/admin/profil", http.StatusSeeOther)
|
http.Redirect(w, r, "/admin/profil", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProfilOtpremiAvatar prima upload lične avatar slike korisnika
|
||||||
|
func (h *Handler) ProfilOtpremiAvatar(w http.ResponseWriter, r *http.Request) {
|
||||||
|
k := middleware.KorisnikIzKonteksta(r.Context())
|
||||||
|
if k == nil {
|
||||||
|
http.Redirect(w, r, "/prijava", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, 2<<20+4096)
|
||||||
|
if err := r.ParseMultipartForm(2 << 20); err != nil {
|
||||||
|
middleware.SetFlash(w, r, h.DB, "greska", "Fajl je prevelik (maksimum 2 MB).")
|
||||||
|
http.Redirect(w, r, "/profil/tema", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fajl, zaglavlje, err := r.FormFile("avatar")
|
||||||
|
if err != nil {
|
||||||
|
middleware.SetFlash(w, r, h.DB, "greska", "Nije odabran fajl.")
|
||||||
|
http.Redirect(w, r, "/profil/tema", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer fajl.Close()
|
||||||
|
|
||||||
|
ext := strings.ToLower(filepath.Ext(zaglavlje.Filename))
|
||||||
|
dozvoljenoExt := map[string]string{
|
||||||
|
".jpg": "image/jpeg",
|
||||||
|
".jpeg": "image/jpeg",
|
||||||
|
".png": "image/png",
|
||||||
|
".webp": "image/webp",
|
||||||
|
}
|
||||||
|
ocekivaniMime, ok := dozvoljenoExt[ext]
|
||||||
|
if !ok {
|
||||||
|
middleware.SetFlash(w, r, h.DB, "greska", "Dozvoljeni formati su JPG, PNG i WebP.")
|
||||||
|
http.Redirect(w, r, "/profil/tema", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 512)
|
||||||
|
n, _ := fajl.Read(buf)
|
||||||
|
stvarniMime := http.DetectContentType(buf[:n])
|
||||||
|
if !strings.HasPrefix(stvarniMime, ocekivaniMime) {
|
||||||
|
middleware.SetFlash(w, r, h.DB, "greska", "Sadržaj fajla ne odgovara odabranoj ekstenziji.")
|
||||||
|
http.Redirect(w, r, "/profil/tema", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err := fajl.Seek(0, io.SeekStart); err != nil {
|
||||||
|
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri obradi fajla.")
|
||||||
|
http.Redirect(w, r, "/profil/tema", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// briše stari avatar sa diska
|
||||||
|
svezi, _ := h.KorisniciRepo.DohvatiPoID(r.Context(), k.ID)
|
||||||
|
if svezi != nil && svezi.AvatarPutanja != "" {
|
||||||
|
deo, _, _ := strings.Cut(svezi.AvatarPutanja, "?")
|
||||||
|
os.Remove(filepath.Join("web/static/uploads", filepath.Base(deo)))
|
||||||
|
}
|
||||||
|
|
||||||
|
novoIme := fmt.Sprintf("korisnik_%d_avatar%s", k.ID, ext)
|
||||||
|
odrediste := filepath.Join("web/static/uploads", novoIme)
|
||||||
|
dst, err := os.Create(odrediste)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("ProfilOtpremiAvatar: ne mogu kreirati fajl", "error", err)
|
||||||
|
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju fajla.")
|
||||||
|
http.Redirect(w, r, "/profil/tema", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer dst.Close()
|
||||||
|
if _, err := io.Copy(dst, fajl); err != nil {
|
||||||
|
slog.Error("ProfilOtpremiAvatar: greška pri kopiranju", "error", err)
|
||||||
|
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju fajla.")
|
||||||
|
http.Redirect(w, r, "/profil/tema", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// cache-buster verzija u URL-u
|
||||||
|
v := fmt.Sprintf("%d", time.Now().Unix())
|
||||||
|
putanja := fmt.Sprintf("/static/uploads/%s?v=%s", novoIme, v)
|
||||||
|
|
||||||
|
if err := h.KorisniciRepo.SacuvajAvatar(r.Context(), k.ID, putanja); err != nil {
|
||||||
|
slog.Error("ProfilOtpremiAvatar: greška pri čuvanju u bazi", "error", err)
|
||||||
|
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri čuvanju.")
|
||||||
|
http.Redirect(w, r, "/profil/tema", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
middleware.SetFlash(w, r, h.DB, "uspeh", "Avatar je sačuvan.")
|
||||||
|
http.Redirect(w, r, "/profil/tema", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProfilUkloniAvatar briše ličnu avatar sliku korisnika
|
||||||
|
func (h *Handler) ProfilUkloniAvatar(w http.ResponseWriter, r *http.Request) {
|
||||||
|
k := middleware.KorisnikIzKonteksta(r.Context())
|
||||||
|
if k == nil {
|
||||||
|
http.Redirect(w, r, "/prijava", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
svezi, _ := h.KorisniciRepo.DohvatiPoID(r.Context(), k.ID)
|
||||||
|
if svezi != nil && svezi.AvatarPutanja != "" {
|
||||||
|
deo, _, _ := strings.Cut(svezi.AvatarPutanja, "?")
|
||||||
|
os.Remove(filepath.Join("web/static/uploads", filepath.Base(deo)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.KorisniciRepo.SacuvajAvatar(r.Context(), k.ID, ""); err != nil {
|
||||||
|
slog.Error("ProfilUkloniAvatar", "error", err)
|
||||||
|
middleware.SetFlash(w, r, h.DB, "greska", "Greška pri uklanjanju avatara.")
|
||||||
|
http.Redirect(w, r, "/profil/tema", http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
middleware.SetFlash(w, r, h.DB, "uspeh", "Avatar je uklonjen.")
|
||||||
|
http.Redirect(w, r, "/profil/tema", http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ type ServisRedDashboard struct {
|
|||||||
|
|
||||||
// ZalihaRed je artikal sa kritičnom zalihom (naziv + količina)
|
// ZalihaRed je artikal sa kritičnom zalihom (naziv + količina)
|
||||||
type ZalihaRed struct {
|
type ZalihaRed struct {
|
||||||
Naziv string
|
Naziv string
|
||||||
Kolicina int
|
Kolicina int
|
||||||
|
KolicinaMin int
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProdajaRedDashboard je jedan red za listu poslednjih prodaja na dashboardu
|
// ProdajaRedDashboard je jedan red za listu poslednjih prodaja na dashboardu
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ type Korisnik struct {
|
|||||||
LokalnaPozadinaBlur string
|
LokalnaPozadinaBlur string
|
||||||
LokalnaPozadinaBlurPozadine string
|
LokalnaPozadinaBlurPozadine string
|
||||||
LokalnaPozadinaGlassOpacity string
|
LokalnaPozadinaGlassOpacity string
|
||||||
|
AvatarPutanja string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sesija predstavlja aktivnu sesiju prijavljenog korisnika
|
// Sesija predstavlja aktivnu sesiju prijavljenog korisnika
|
||||||
|
|||||||
@@ -36,9 +36,11 @@ type PodaciStranice struct {
|
|||||||
Tema string
|
Tema string
|
||||||
NazivFirme string
|
NazivFirme string
|
||||||
Podnazlov string
|
Podnazlov string
|
||||||
LogoTip string // "sa_nazivom", "bez_naziva", "slika"
|
LogoPutanja string // putanja do slike loga firme
|
||||||
LogoPutanja string // putanja do slike, koristi se samo kada je LogoTip "slika"
|
TopbarLogoSlika bool // prikaži logo sliku u topbaru
|
||||||
Korisnik string
|
TopbarLogoTekst bool // prikaži naziv firme u topbaru
|
||||||
|
AvatarPutanja string // putanja do lične avatar slike korisnika
|
||||||
|
Korisnik string
|
||||||
KorisnikIme string // korisničko ime prijavljenog korisnika
|
KorisnikIme string // korisničko ime prijavljenog korisnika
|
||||||
KorisnikUloga string // uloga: "superadmin", "admin", "radnik"
|
KorisnikUloga string // uloga: "superadmin", "admin", "radnik"
|
||||||
CsrfToken string // CSRF zaštitni token za forme
|
CsrfToken string // CSRF zaštitni token za forme
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- topbar podešavanja: switch za logo sliku i switch za naziv firme
|
||||||
|
INSERT OR IGNORE INTO podesavanja (kljuc, vrednost) VALUES
|
||||||
|
('topbar_logo_slika', '1'),
|
||||||
|
('topbar_logo_tekst', '1');
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- lična avatar slika korisnika (prazno = koristi inicijale)
|
||||||
|
ALTER TABLE korisnici ADD COLUMN avatar_putanja TEXT NOT NULL DEFAULT '';
|
||||||
+53
-6
@@ -303,12 +303,28 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.topbar-naslov {
|
.topbar-naslov {
|
||||||
font-weight: 500;
|
font-weight: 600;
|
||||||
font-size: 15px;
|
font-size: 18px;
|
||||||
color: var(--tekst-glavni);
|
color: var(--tekst-glavni);
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* identifikacija firme u topbaru (naziv + logo) — pomera se i može da se skrati */
|
||||||
|
.topbar-firma { flex-shrink: 0; line-height: 1.2; min-width: 0; }
|
||||||
|
.topbar-firma .topbar-firma-naziv {
|
||||||
|
font-weight: 700; font-size: 14px; color: var(--tekst-glavni); letter-spacing: -0.2px;
|
||||||
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||||
|
}
|
||||||
|
.topbar-firma .topbar-firma-podnaziv {
|
||||||
|
font-size: 11px; color: var(--tekst-sporedni);
|
||||||
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||||
|
}
|
||||||
|
.topbar-logo { height: 34px; width: auto; border-radius: 6px; flex-shrink: 0; }
|
||||||
|
|
||||||
/* sadržaj stranice */
|
/* sadržaj stranice */
|
||||||
.sadrzaj {
|
.sadrzaj {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -583,7 +599,7 @@ body {
|
|||||||
.avatar-korisnik {
|
.avatar-korisnik {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
border-radius: 50%;
|
border-radius: 9px;
|
||||||
background: var(--sb-akcent);
|
background: var(--sb-akcent);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -918,6 +934,11 @@ select {
|
|||||||
#hamburger-topbar { display: flex !important; color: var(--tekst-glavni); }
|
#hamburger-topbar { display: flex !important; color: var(--tekst-glavni); }
|
||||||
#hamburger-topbar:hover { background: var(--pozadina); }
|
#hamburger-topbar:hover { background: var(--pozadina); }
|
||||||
|
|
||||||
|
/* na telefonu sklanjamo identifikaciju firme iz topbara — naziv i logo su
|
||||||
|
već vidljivi u bočnom meniju, pa naslov stranice dobija ceo prostor */
|
||||||
|
.topbar-firma, .topbar-logo { display: none; }
|
||||||
|
.topbar-naslov { font-size: 16px; }
|
||||||
|
|
||||||
/* teme */
|
/* teme */
|
||||||
.topbar-teme { display: none; }
|
.topbar-teme { display: none; }
|
||||||
.teme-grid { flex-direction: column !important; }
|
.teme-grid { flex-direction: column !important; }
|
||||||
@@ -1017,6 +1038,13 @@ select {
|
|||||||
.stranica-stack .animiraj:nth-child(4) { animation-delay: 0.28s; }
|
.stranica-stack .animiraj:nth-child(4) { animation-delay: 0.28s; }
|
||||||
.stranica-stack .animiraj:nth-child(5) { animation-delay: 0.34s; }
|
.stranica-stack .animiraj:nth-child(5) { animation-delay: 0.34s; }
|
||||||
|
|
||||||
|
/* Dashboard stat kartice — delay da ne krenemo pre nego što view-transition završi */
|
||||||
|
.dash-stat.animiraj:nth-child(1) { animation-delay: 0.08s; }
|
||||||
|
.dash-stat.animiraj:nth-child(2) { animation-delay: 0.13s; }
|
||||||
|
.dash-stat.animiraj:nth-child(3) { animation-delay: 0.18s; }
|
||||||
|
.dash-stat.animiraj:nth-child(4) { animation-delay: 0.23s; }
|
||||||
|
.dash-stat.animiraj:nth-child(5) { animation-delay: 0.28s; }
|
||||||
|
|
||||||
/* Bedž statusa servisnog naloga — JEDNO mesto za izgled i boje statusa (lista i detalji).
|
/* Bedž statusa servisnog naloga — JEDNO mesto za izgled i boje statusa (lista i detalji).
|
||||||
Mora biti u main.css: HTMX navigacija odbacuje <head>, pa page <style> ne bi važio. */
|
Mora biti u main.css: HTMX navigacija odbacuje <head>, pa page <style> ne bi važio. */
|
||||||
.status-badge { display: inline-block; padding: 3px 10px; border-radius: 20px; font-size: 12px; font-weight: 500; white-space: nowrap; }
|
.status-badge { display: inline-block; padding: 3px 10px; border-radius: 20px; font-size: 12px; font-weight: 500; white-space: nowrap; }
|
||||||
@@ -1153,11 +1181,30 @@ select {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sprečava treperenje pozadine pri navigaciji između stranica (Chrome/Opera/noviji Firefox) */
|
/* sprečava treperenje pozadine pri navigaciji između stranica (samo browseri koji podržavaju) */
|
||||||
@view-transition {
|
@supports (view-transition-name: none) {
|
||||||
navigation: auto;
|
@view-transition {
|
||||||
|
navigation: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* iOS-style toggle switch (.toggl > input[type=checkbox] + .toggl-klizac) */
|
||||||
|
.toggl { position:relative; display:inline-block; width:44px; height:26px; flex-shrink:0; }
|
||||||
|
.toggl input { opacity:0; width:0; height:0; position:absolute; }
|
||||||
|
.toggl-klizac {
|
||||||
|
position:absolute; inset:0; border-radius:26px; cursor:pointer;
|
||||||
|
background:var(--ivica); transition:background 0.2s;
|
||||||
|
}
|
||||||
|
.toggl-klizac::before {
|
||||||
|
content:''; position:absolute;
|
||||||
|
width:20px; height:20px; border-radius:50%;
|
||||||
|
left:3px; top:3px;
|
||||||
|
background:#fff; box-shadow:0 1px 3px rgba(0,0,0,0.25);
|
||||||
|
transition:transform 0.2s;
|
||||||
|
}
|
||||||
|
.toggl input:checked + .toggl-klizac { background:var(--sb-akcent); }
|
||||||
|
.toggl input:checked + .toggl-klizac::before { transform:translateX(18px); }
|
||||||
|
|
||||||
/* pomoćne klase (ranije iz Tailwind-a, sada lokalno da ne zavisimo od CDN-a) */
|
/* pomoćne klase (ranije iz Tailwind-a, sada lokalno da ne zavisimo od CDN-a) */
|
||||||
.grid { display: grid; }
|
.grid { display: grid; }
|
||||||
.gap-3 { gap: 12px; }
|
.gap-3 { gap: 12px; }
|
||||||
|
|||||||
@@ -9,18 +9,10 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<div class="logo-zona">
|
<div class="logo-zona">
|
||||||
{{if eq .LogoTip "slika"}}
|
<div>
|
||||||
<img src="{{.LogoPutanja}}" alt="Logo"
|
<div class="logo-naziv">{{.NazivFirme}}</div>
|
||||||
style="max-height:48px;width:auto;border-radius:8px;padding:4px;background:var(--kartica);box-shadow:var(--senka);transition:transform 0.2s;flex-shrink:0;"
|
{{if .Podnazlov}}<div class="logo-podnazlov">{{.Podnazlov}}</div>{{end}}
|
||||||
onmouseover="this.style.transform='scale(1.02)'"
|
</div>
|
||||||
onmouseout="this.style.transform='scale(1)'">
|
|
||||||
{{else if eq .LogoTip "bez_naziva"}}
|
|
||||||
{{else}}
|
|
||||||
<div>
|
|
||||||
<div class="logo-naziv">{{.NazivFirme}}</div>
|
|
||||||
<div class="logo-podnazlov">{{.Podnazlov}}</div>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -10,11 +10,19 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{{if and .TopbarLogoSlika .LogoPutanja}}
|
||||||
|
<img src="{{.LogoPutanja}}" alt="Logo" class="topbar-logo">
|
||||||
|
{{end}}
|
||||||
|
|
||||||
<span class="topbar-naslov">{{.NaslovStranice}}</span>
|
<span class="topbar-naslov">{{.NaslovStranice}}</span>
|
||||||
|
|
||||||
<div style="position:relative;" id="avatar-wrapper">
|
<div style="position:relative;" id="avatar-wrapper">
|
||||||
<div class="avatar-korisnik" id="avatar-dugme" style="cursor:pointer;" title="{{.Korisnik}}">
|
<div class="avatar-korisnik" id="avatar-dugme" style="cursor:pointer;overflow:hidden;" title="{{.Korisnik}}">
|
||||||
|
{{if .AvatarPutanja}}
|
||||||
|
<img src="{{.AvatarPutanja}}" alt="Avatar" style="width:100%;height:100%;object-fit:cover;display:block;">
|
||||||
|
{{else}}
|
||||||
{{if .Korisnik}}{{slice .Korisnik 0 2}}{{else}}NT{{end}}
|
{{if .Korisnik}}{{slice .Korisnik 0 2}}{{else}}NT{{end}}
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div id="avatar-meni" style="display:none;position:fixed;background:var(--kartica);backdrop-filter:none;-webkit-backdrop-filter:none;border:0.5px solid var(--ivica);border-radius:10px;box-shadow:var(--senka);min-width:160px;z-index:9999;overflow:hidden;transform-origin:top right;">
|
<div id="avatar-meni" style="display:none;position:fixed;background:var(--kartica);backdrop-filter:none;-webkit-backdrop-filter:none;border:0.5px solid var(--ivica);border-radius:10px;box-shadow:var(--senka);min-width:160px;z-index:9999;overflow:hidden;transform-origin:top right;">
|
||||||
<a href="/admin/profil" style="display:flex;align-items:center;gap:10px;padding:10px 14px;font-size:14px;color:var(--tekst-glavni);text-decoration:none;transition:background 0.15s;"
|
<a href="/admin/profil" style="display:flex;align-items:center;gap:10px;padding:10px 14px;font-size:14px;color:var(--tekst-glavni);text-decoration:none;transition:background 0.15s;"
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
<div class="grid grid-cols-2 md:grid-cols-5 gap-3 mb-6">
|
<div class="grid grid-cols-2 md:grid-cols-5 gap-3 mb-6">
|
||||||
<!-- stat kartice -->
|
<!-- stat kartice -->
|
||||||
<div class="kartica dash-stat animiraj">
|
<a href="/magacin" class="kartica dash-stat animiraj" style="text-decoration:none;display:block;cursor:pointer;">
|
||||||
<div style="width:36px;height:36px;border-radius:8px;background:#eff2ff;display:flex;align-items:center;justify-content:center;margin-bottom:10px;">
|
<div style="width:36px;height:36px;border-radius:8px;background:#eff2ff;display:flex;align-items:center;justify-content:center;margin-bottom:10px;">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#4f7ef8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#4f7ef8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
|
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
|
||||||
@@ -29,9 +29,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style="font-size:22px;font-weight:500;color:var(--tekst-glavni);">{{ .BrojArtikala }}</div>
|
<div style="font-size:22px;font-weight:500;color:var(--tekst-glavni);">{{ .BrojArtikala }}</div>
|
||||||
<div style="font-size:14px;color:var(--tekst-glavni);margin-top:4px;font-weight:500;">Artikala na stanju</div>
|
<div style="font-size:14px;color:var(--tekst-glavni);margin-top:4px;font-weight:500;">Artikala na stanju</div>
|
||||||
</div>
|
</a>
|
||||||
|
|
||||||
<div class="kartica dash-stat animiraj">
|
<a href="/servis" class="kartica dash-stat animiraj" style="text-decoration:none;display:block;cursor:pointer;">
|
||||||
<div style="width:36px;height:36px;border-radius:8px;background:#f0fdf4;display:flex;align-items:center;justify-content:center;margin-bottom:10px;">
|
<div style="width:36px;height:36px;border-radius:8px;background:#f0fdf4;display:flex;align-items:center;justify-content:center;margin-bottom:10px;">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#16a34a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#16a34a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z" />
|
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z" />
|
||||||
@@ -39,10 +39,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style="font-size:22px;font-weight:500;color:var(--tekst-glavni);">{{ .AktivniServisi }}</div>
|
<div style="font-size:22px;font-weight:500;color:var(--tekst-glavni);">{{ .AktivniServisi }}</div>
|
||||||
<div style="font-size:14px;color:var(--tekst-glavni);margin-top:4px;font-weight:500;">Aktivnih servisa</div>
|
<div style="font-size:14px;color:var(--tekst-glavni);margin-top:4px;font-weight:500;">Aktivnih servisa</div>
|
||||||
</div>
|
</a>
|
||||||
|
|
||||||
{{ if index .Dozvole "dashboard.prihod" }}
|
{{ if index .Dozvole "dashboard.prihod" }}
|
||||||
<div class="kartica dash-stat animiraj">
|
<a href="/izvestaji" class="kartica dash-stat animiraj" style="text-decoration:none;display:block;cursor:pointer;">
|
||||||
<div style="width:36px;height:36px;border-radius:8px;background:#fff7ed;display:flex;align-items:center;justify-content:center;margin-bottom:10px;">
|
<div style="width:36px;height:36px;border-radius:8px;background:#fff7ed;display:flex;align-items:center;justify-content:center;margin-bottom:10px;">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#ea580c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#ea580c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<circle cx="9" cy="21" r="1" />
|
<circle cx="9" cy="21" r="1" />
|
||||||
@@ -52,10 +52,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style="font-size:22px;font-weight:500;color:var(--tekst-glavni);">{{ printf "%.0f" .PrihodOvogMeseca }} din</div>
|
<div style="font-size:22px;font-weight:500;color:var(--tekst-glavni);">{{ printf "%.0f" .PrihodOvogMeseca }} din</div>
|
||||||
<div style="font-size:14px;color:var(--tekst-glavni);margin-top:4px;font-weight:500;">Prihod ovog meseca</div>
|
<div style="font-size:14px;color:var(--tekst-glavni);margin-top:4px;font-weight:500;">Prihod ovog meseca</div>
|
||||||
</div>
|
</a>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
<div class="kartica dash-stat animiraj">
|
<a href="/magacin?kriticni=1" class="kartica dash-stat animiraj" style="text-decoration:none;display:block;cursor:pointer;">
|
||||||
<div style="width:36px;height:36px;border-radius:8px;background:#fef2f2;display:flex;align-items:center;justify-content:center;margin-bottom:10px;">
|
<div style="width:36px;height:36px;border-radius:8px;background:#fef2f2;display:flex;align-items:center;justify-content:center;margin-bottom:10px;">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
|
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
|
||||||
@@ -65,9 +65,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style="font-size:22px;font-weight:500;color:var(--tekst-glavni);">{{ .KriticnaZaliha }}</div>
|
<div style="font-size:22px;font-weight:500;color:var(--tekst-glavni);">{{ .KriticnaZaliha }}</div>
|
||||||
<div style="font-size:14px;color:var(--tekst-glavni);margin-top:4px;font-weight:500;">Kritično niska zaliha</div>
|
<div style="font-size:14px;color:var(--tekst-glavni);margin-top:4px;font-weight:500;">Kritično niska zaliha</div>
|
||||||
</div>
|
</a>
|
||||||
|
|
||||||
<a href="/podsetnici" class="kartica dash-stat animiraj" style="text-decoration:none;display:block;">
|
<a href="/podsetnici" class="kartica dash-stat animiraj" style="text-decoration:none;display:block;cursor:pointer;">
|
||||||
<div style="width:36px;height:36px;border-radius:8px;background:#f0f9ff;display:flex;align-items:center;justify-content:center;margin-bottom:10px;">
|
<div style="width:36px;height:36px;border-radius:8px;background:#f0f9ff;display:flex;align-items:center;justify-content:center;margin-bottom:10px;">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#0284c7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#0284c7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" />
|
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" />
|
||||||
@@ -100,7 +100,6 @@
|
|||||||
<div class="kartica dash-kartica animiraj">
|
<div class="kartica dash-kartica animiraj">
|
||||||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:14px;">
|
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:14px;">
|
||||||
<span style="font-size:14px;font-weight:500;color:var(--tekst-glavni);">Kritične zalihe</span>
|
<span style="font-size:14px;font-weight:500;color:var(--tekst-glavni);">Kritične zalihe</span>
|
||||||
<span style="font-size:11px;padding:2px 8px;border-radius:20px;background:rgba(220,38,38,0.15);color:#ef4444;font-weight:500;">Upozorenje</span>
|
|
||||||
</div>
|
</div>
|
||||||
{{ range .KriticneZalihe }}
|
{{ range .KriticneZalihe }}
|
||||||
<div style="display:flex;align-items:center;gap:10px;padding:8px 0;border-bottom:0.5px solid var(--ivica);">
|
<div style="display:flex;align-items:center;gap:10px;padding:8px 0;border-bottom:0.5px solid var(--ivica);">
|
||||||
@@ -136,4 +135,4 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|||||||
@@ -11,15 +11,23 @@
|
|||||||
|
|
||||||
<!-- upload loga — posebna forma jer je multipart, mora biti van glavne forme -->
|
<!-- upload loga — posebna forma jer je multipart, mora biti van glavne forme -->
|
||||||
<form method="POST" action="/podesavanja/logo" enctype="multipart/form-data">
|
<form method="POST" action="/podesavanja/logo" enctype="multipart/form-data">
|
||||||
|
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
||||||
<div class="kartica animiraj" style="margin-bottom:16px;">
|
<div class="kartica animiraj" style="margin-bottom:16px;">
|
||||||
<div style="display:flex;align-items:center;gap:10px;margin-bottom:16px;padding-bottom:12px;border-bottom:0.5px solid var(--ivica);">
|
<div style="display:flex;align-items:center;gap:10px;margin-bottom:16px;padding-bottom:12px;border-bottom:0.5px solid var(--ivica);">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="var(--sb-akcent)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="var(--sb-akcent)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
|
||||||
<span style="font-size:15px;font-weight:500;color:var(--tekst-glavni);">Logo firme</span>
|
<span style="font-size:15px;font-weight:500;color:var(--tekst-glavni);">Logo firme</span>
|
||||||
</div>
|
</div>
|
||||||
{{if .LogoPutanja}}
|
{{if .LogoPutanja}}
|
||||||
<div style="margin-bottom:12px;">
|
<div style="display:flex;align-items:center;gap:12px;margin-bottom:12px;">
|
||||||
<img src="{{.LogoPutanja}}" alt="Trenutni logo"
|
<img src="{{.LogoPutanja}}" alt="Trenutni logo"
|
||||||
style="max-height:60px;max-width:200px;object-fit:contain;border:0.5px solid var(--ivica);border-radius:8px;padding:6px;background:var(--kartica);">
|
style="max-height:60px;max-width:200px;object-fit:contain;border:0.5px solid var(--ivica);border-radius:8px;padding:6px;background:var(--kartica);flex-shrink:0;">
|
||||||
|
<form method="POST" action="/podesavanja/logo/ukloni" style="margin:0;">
|
||||||
|
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
||||||
|
<button type="submit"
|
||||||
|
style="padding:6px 14px;background:none;border:0.5px solid #dc2626;color:#dc2626;border-radius:8px;font-size:13px;cursor:pointer;white-space:nowrap;">
|
||||||
|
Ukloni sliku
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;">
|
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;">
|
||||||
@@ -43,6 +51,7 @@
|
|||||||
|
|
||||||
<!-- firma: naziv, podnazlov, adresa, telefon, PIB, logo zona -->
|
<!-- firma: naziv, podnazlov, adresa, telefon, PIB, logo zona -->
|
||||||
<form method="POST" action="/podesavanja/sacuvaj">
|
<form method="POST" action="/podesavanja/sacuvaj">
|
||||||
|
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
||||||
<input type="hidden" name="_next" value="/admin/podesavanja/opste">
|
<input type="hidden" name="_next" value="/admin/podesavanja/opste">
|
||||||
<div class="kartica animiraj" style="margin-bottom:16px;">
|
<div class="kartica animiraj" style="margin-bottom:16px;">
|
||||||
<div style="display:flex;align-items:center;gap:10px;margin-bottom:16px;padding-bottom:12px;border-bottom:0.5px solid var(--ivica);">
|
<div style="display:flex;align-items:center;gap:10px;margin-bottom:16px;padding-bottom:12px;border-bottom:0.5px solid var(--ivica);">
|
||||||
@@ -87,22 +96,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div style="display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border:0.5px solid var(--ivica);border-radius:8px;">
|
||||||
<label style="font-size:13px;color:var(--tekst-sporedni);display:block;margin-bottom:8px;">Logo zona</label>
|
<div>
|
||||||
<div style="display:flex;gap:10px;flex-wrap:wrap;">
|
<div style="font-size:13px;font-weight:500;color:var(--tekst-glavni);">Prikaži logo u gornjoj traci</div>
|
||||||
<label style="display:flex;align-items:center;gap:8px;padding:10px 14px;border:0.5px solid var(--ivica);border-radius:8px;cursor:pointer;flex:1;{{if or (eq .LogoTip "sa_nazivom") (eq .LogoTip "tekst") (eq .LogoTip "ikonica") (not .LogoTip)}}border-color:var(--sb-akcent);background:var(--pozadina);{{end}}">
|
<div style="font-size:12px;color:var(--tekst-sporedni);">Logo slika se prikazuje pored naslova stranice</div>
|
||||||
<input type="radio" name="logo_tip" value="sa_nazivom" {{if or (eq .LogoTip "sa_nazivom") (eq .LogoTip "tekst") (eq .LogoTip "ikonica") (not .LogoTip)}}checked{{end}} style="accent-color:var(--sb-akcent);">
|
|
||||||
<span style="font-size:13px;color:var(--tekst-glavni);">Sa nazivom</span>
|
|
||||||
</label>
|
|
||||||
<label style="display:flex;align-items:center;gap:8px;padding:10px 14px;border:0.5px solid var(--ivica);border-radius:8px;cursor:pointer;flex:1;{{if eq .LogoTip "bez_naziva"}}border-color:var(--sb-akcent);background:var(--pozadina);{{end}}">
|
|
||||||
<input type="radio" name="logo_tip" value="bez_naziva" {{if eq .LogoTip "bez_naziva"}}checked{{end}} style="accent-color:var(--sb-akcent);">
|
|
||||||
<span style="font-size:13px;color:var(--tekst-glavni);">Bez naziva</span>
|
|
||||||
</label>
|
|
||||||
<label style="display:flex;align-items:center;gap:8px;padding:10px 14px;border:0.5px solid var(--ivica);border-radius:8px;flex:1;{{if eq .LogoTip "slika"}}border-color:var(--sb-akcent);background:var(--pozadina);{{end}}{{if not .LogoPutanja}}opacity:0.45;cursor:not-allowed;{{else}}cursor:pointer;{{end}}">
|
|
||||||
<input type="radio" name="logo_tip" value="slika" {{if eq .LogoTip "slika"}}checked{{end}} {{if not .LogoPutanja}}disabled{{end}} style="accent-color:var(--sb-akcent);">
|
|
||||||
<span style="font-size:13px;color:var(--tekst-glavni);">Sa logom</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
<label class="toggl">
|
||||||
|
<input type="checkbox" name="topbar_logo_slika" value="1" {{if .TopbarLogoSlika}}checked{{end}}>
|
||||||
|
<span class="toggl-klizac"></span>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -119,6 +121,7 @@
|
|||||||
|
|
||||||
<!-- profil firme: pravni/poreski status (Faza 0) — određuje koji se zakonski moduli pale -->
|
<!-- profil firme: pravni/poreski status (Faza 0) — određuje koji se zakonski moduli pale -->
|
||||||
<form method="POST" action="/podesavanja/sacuvaj">
|
<form method="POST" action="/podesavanja/sacuvaj">
|
||||||
|
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
||||||
<input type="hidden" name="_next" value="/admin/podesavanja/opste">
|
<input type="hidden" name="_next" value="/admin/podesavanja/opste">
|
||||||
<div class="kartica animiraj" style="margin-bottom:16px;">
|
<div class="kartica animiraj" style="margin-bottom:16px;">
|
||||||
<div style="display:flex;align-items:center;gap:10px;margin-bottom:16px;padding-bottom:12px;border-bottom:0.5px solid var(--ivica);">
|
<div style="display:flex;align-items:center;gap:10px;margin-bottom:16px;padding-bottom:12px;border-bottom:0.5px solid var(--ivica);">
|
||||||
|
|||||||
@@ -74,6 +74,54 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- kartica: moj avatar -->
|
||||||
|
<div class="kartica animiraj" style="margin-bottom:16px;">
|
||||||
|
<div style="display:flex;align-items:center;gap:10px;margin-bottom:16px;padding-bottom:12px;border-bottom:0.5px solid var(--ivica);">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="var(--sb-akcent)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
||||||
|
<span style="font-size:15px;font-weight:500;color:var(--tekst-glavni);">Moj avatar</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display:flex;align-items:center;gap:16px;margin-bottom:16px;">
|
||||||
|
<div style="width:64px;height:64px;border-radius:10px;overflow:hidden;border:0.5px solid var(--ivica);flex-shrink:0;background:var(--pozadina);display:flex;align-items:center;justify-content:center;">
|
||||||
|
{{if .AvatarPutanja}}
|
||||||
|
<img src="{{.AvatarPutanja}}" alt="Moj avatar" style="width:100%;height:100%;object-fit:cover;">
|
||||||
|
{{else}}
|
||||||
|
<span style="font-size:22px;font-weight:600;color:var(--tekst-sporedni);">{{if .KorisnikIme}}{{slice .KorisnikIme 0 2}}{{else}}NT{{end}}</span>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
<div style="font-size:13px;color:var(--tekst-sporedni);line-height:1.5;">
|
||||||
|
Slika se prikazuje kao dugme u gornjoj traci.<br>
|
||||||
|
Dozvoljeni formati: JPG, PNG, WebP. Maksimum 2 MB.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{if .AvatarPutanja}}
|
||||||
|
<form method="POST" action="/profil/avatar/ukloni" style="display:inline;margin-bottom:12px;">
|
||||||
|
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
||||||
|
<button type="submit" style="background:none;border:0.5px solid #dc2626;color:#dc2626;padding:6px 14px;border-radius:8px;font-size:13px;cursor:pointer;margin-bottom:12px;">
|
||||||
|
Ukloni avatar
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<form method="POST" action="/profil/avatar" enctype="multipart/form-data">
|
||||||
|
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
||||||
|
<div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;">
|
||||||
|
<input type="file" id="avatar-file" name="avatar" accept=".jpg,.jpeg,.png,.webp"
|
||||||
|
style="display:none;" onchange="document.getElementById('avatar-ime').textContent=this.files[0]?this.files[0].name:'Nijedan fajl nije izabran'">
|
||||||
|
<label for="avatar-file"
|
||||||
|
style="display:inline-flex;align-items:center;gap:6px;padding:8px 14px;border:0.5px solid var(--ivica);border-radius:8px;cursor:pointer;font-size:13px;color:var(--tekst-glavni);background:var(--pozadina);">
|
||||||
|
Izaberi sliku
|
||||||
|
</label>
|
||||||
|
<span id="avatar-ime" class="pomocni-tekst">Nijedan fajl nije izabran</span>
|
||||||
|
<button type="submit"
|
||||||
|
style="background:var(--sb-akcent);color:#fff;border:none;padding:8px 16px;border-radius:8px;font-size:13px;cursor:pointer;">
|
||||||
|
Otpremi avatar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- kartica: moja pozadinska slika -->
|
<!-- kartica: moja pozadinska slika -->
|
||||||
<div class="kartica animiraj" style="margin-bottom:16px;">
|
<div class="kartica animiraj" style="margin-bottom:16px;">
|
||||||
<div style="display:flex;align-items:center;gap:10px;margin-bottom:16px;padding-bottom:12px;border-bottom:0.5px solid var(--ivica);">
|
<div style="display:flex;align-items:center;gap:10px;margin-bottom:16px;padding-bottom:12px;border-bottom:0.5px solid var(--ivica);">
|
||||||
|
|||||||
@@ -14,8 +14,6 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{{if .AppPozadina}}<link rel="preload" as="image" href="{{.AppPozadina}}">{{end}}
|
|
||||||
|
|
||||||
<!-- tema — učitava se prva -->
|
<!-- tema — učitava se prva -->
|
||||||
<link rel="stylesheet" href="/static/css/teme/{{.Tema}}.css?v={{.AssetV}}" />
|
<link rel="stylesheet" href="/static/css/teme/{{.Tema}}.css?v={{.AssetV}}" />
|
||||||
|
|
||||||
@@ -24,6 +22,8 @@
|
|||||||
|
|
||||||
{{block "dodatni-css" .}}{{end}}
|
{{block "dodatni-css" .}}{{end}}
|
||||||
|
|
||||||
|
{{if .AppPozadina}}<link rel="preload" as="image" href="{{.AppPozadina}}">{{end}}
|
||||||
|
|
||||||
{{if .AppPozadina}}
|
{{if .AppPozadina}}
|
||||||
<style>
|
<style>
|
||||||
html {
|
html {
|
||||||
|
|||||||
Reference in New Issue
Block a user