Compare commits

...

2 Commits

Author SHA1 Message Date
julien
2cdee0eed1 Merge branch 'main' of https://lab.canguidev.fr/cangui/shelfy 2025-06-20 15:41:41 +02:00
julien
1201e4ae83 add folders 2025-06-20 15:41:37 +02:00
6 changed files with 238 additions and 24 deletions

View File

@ -220,6 +220,9 @@ func RoutesProtected(r *mux.Router, bd *gorm.DB) {
//API Check path
r.HandleFunc("/validate-path", download.PathValidationHandler)
r.HandleFun("/folders", renders.StreamHandler)
r.HandleFun("/folders/detail", renders.DetailHandler)
//API Scan folder
}

View File

@ -20,7 +20,58 @@ import (
"gorm.io/gorm"
)
var (
// templates contiendra TOUTES vos pages .pages.tmpl
templates *template.Template
)
func init() {
// 1. Définissez votre FuncMap
funcMap := template.FuncMap{
"hasSuffix": strings.HasSuffix,
"ext": func(name string) string {
return strings.TrimPrefix(filepath.Ext(name), ".")
},
"split": strings.Split,
"trimPrefix": strings.TrimPrefix,
}
// 2. Parsez tous les templates EN UNE FOIS, en injectant le FuncMap
templates = template.Must(
template.New("").
Funcs(funcMap).
// ajuste le chemin vers vos .pages.tmpl
ParseGlob("./templates/*.pages.tmpl"),
)
}
type Entry struct {
Name, Path string
IsDir bool
ModTime time.Time
Size int64
}
// helper pour lister un dossier
func listEntries(base, rel string) ([]Entry, error) {
dir := filepath.Join(base, rel)
fis, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
out := make([]Entry, 0, len(fis))
for _, fi := range fis {
info, _ := fi.Info()
out = append(out, Entry{
Name: fi.Name(),
Path: filepath.ToSlash(filepath.Join(rel, fi.Name())),
IsDir: fi.IsDir(),
ModTime: info.ModTime(),
Size: info.Size(),
})
}
return out, nil
}
func Login(w http.ResponseWriter, r *http.Request){
renderTemplate(w,"login",nil)
}
@ -576,37 +627,72 @@ func HandleDeleteMultipleJobs(db *gorm.DB) http.HandlerFunc {
}
}
func StreamHandler(w http.ResponseWriter, r *http.Request) {
base := "/app/upload"
cur := r.URL.Query().Get("path") // ex: "", "Icons", "Code/Sub"
// 1) sidebar : on ne gère que le niveau racine
root, _ := listEntries(base, "")
var dirs []Entry
for _, e := range root {
if e.IsDir {
dirs = append(dirs, e)
}
}
// 2) contenu courant
entries, _ := listEntries(base, cur)
data := map[string]interface{}{
"Dirs": dirs,
"Entries": entries,
"CurrentPath": cur,
}
// Si cest un appel HTMX (liste partielle) : on renvoie juste la grille
if r.Header.Get("HX-Request") == "true" {
renderPartial(w, "_file_list", data)
return
}
// Sinon on rend la page complète
renderTemplate(w, "folders", data)
}
func DetailHandler(w http.ResponseWriter, r *http.Request) {
base := "static/uploads"
rel := r.URL.Query().Get("path")
info, err := os.Stat(filepath.Join(base, rel))
if err != nil {
http.NotFound(w, r)
return
}
entry := Entry{
Name: info.Name(),
Path: rel,
IsDir: info.IsDir(),
ModTime: info.ModTime(),
Size: info.Size(),
}
// Toujours partial HTMX
renderPartial(w, "_file_detail", map[string]interface{}{
"Entry": entry,
})
}
func renderPartial(w http.ResponseWriter, templ string, data map[string]interface{}) {
t, err := template.ParseFiles("./templates/" + templ + ".pages.tmpl")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err = t.Execute(w, data)
if err != nil {
// Exécute directement le define `<templ>.pages.tmpl`
if err := templates.ExecuteTemplate(w, templ+".pages.tmpl", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func renderTemplate(w http.ResponseWriter, templ string, data map[string]interface{}) {
t, err := template.ParseFiles(
"./templates/head.pages.tmpl", // Template inclus
"./templates/" + templ + ".pages.tmpl", // Template principal
)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Exécutez explicitement le template principal
err = t.ExecuteTemplate(w, templ+".pages.tmpl", data)
if err != nil {
// Pareil, on exécute le principal
if err := templates.ExecuteTemplate(w, templ+".pages.tmpl", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

View File

@ -0,0 +1,14 @@
<div class="box">
{{ with .Entry }}
<h4 class="title is-5">{{ .Name }}</h4>
<p><strong>Type :</strong> {{ ext .Name }}</p>
<p><strong>Taille :</strong> {{ printf "%.1f KB" (float64 .Size/1024) }}</p>
<p><strong>Modifié le :</strong> {{ .ModTime.Format "02 Jan 2006 15:04" }}</p>
{{ if and (not .IsDir) (or (hasSuffix .Name ".jpg") (hasSuffix .Name ".png")) }}
<figure class="image">
<img src="/app/upload/{{ .Path }}">
</figure>
{{ end }}
{{ end }}
</div>

View File

@ -0,0 +1,30 @@
<div class="columns is-multiline">
{{- range .Entries }}
<div class="column is-4-mobile is-3-tablet">
<div class="card"
hx-get="{{ if .IsDir }}
/folders?path={{ urlquery .Path }}
{{ else }}
/folders/detail?path={{ urlquery .Path }}
{{ end }}"
hx-target="{{ if .IsDir }}#file-list{{ else }}#detail-panel{{ end }}"
hx-push-url="true"
style="cursor:pointer">
<div class="card-image">
<figure class="image is-4by3">
{{ if .IsDir }}
<i class="fas fa-folder fa-3x has-text-warning" style="margin:1rem"></i>
{{ else if or (hasSuffix .Name ".jpg") (hasSuffix .Name ".png") }}
<img src="/app/upload/{{ .Path }}" alt="{{ .Name }}">
{{ else }}
<i class="fas fa-file fa-3x"></i>
{{ end }}
</figure>
</div>
<div class="card-content">
<p class="title is-6">{{ .Name }}</p>
</div>
</div>
</div>
{{- end }}
</div>

View File

@ -43,6 +43,7 @@
</li>
</ul>
</li>
<li><a hx-get="/folders" class="nav-link" hx-target="#content" hx-swap-oob="beforeend">Folders</a></li>
<li><a hx-get="/settings" class="nav-link" hx-target="#content" hx-swap-oob="beforeend">Settings</a></li>
</li>
</ul>

View File

@ -0,0 +1,80 @@
<section class="section">
<div class="container is-fluid">
<div class="columns">
<!-- Sidebar -->
<div class="column is-2">
<aside class="menu">
<p class="menu-label">Dossiers</p>
<ul class="menu-list">
{{- range .Dirs }}
<li>
<a
hx-get="/folders?path={{ urlquery .Name }}"
hx-target="#file-list"
hx-push-url="true"
class="{{ if eq $.CurrentPath .Name }}is-active{{ end }}"
>
{{ .Name }}
</a>
</li>
{{- end }}
</ul>
</aside>
</div>
<!-- Grille -->
<div class="column is-7">
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li>
<a
hx-get="/folders"
hx-target="#file-list"
hx-push-url="true"
>
Home
</a>
</li>
{{- $parts := split .CurrentPath "/" }}
{{- $acc := "" }}
{{- range $i, $p := $parts }}
{{- if ne $p "" }}
{{- $acc = printf "%s/%s" $acc $p }}
<li class="is-active">
<a
hx-get="/folders?path={{ urlquery (trimPrefix "/" $acc) }}"
hx-target="#file-list"
hx-push-url="true"
>
{{ $p }}
</a>
</li>
{{- end }}
{{- end }}
</ul>
</nav>
<div
id="file-list"
hx-trigger="load"
hx-get="/folders?path={{ urlquery .CurrentPath }}"
hx-target="#file-list"
>
{{ template "_file_list.pages.tmpl" . }}
</div>
</div>
<!-- Détails -->
<div class="column is-3">
<div id="detail-panel">
<em>Sélectionnez un fichier ou dossier…</em>
</div>
</div>
</div>
</div>
</section>