Pagina utenti caricata da json

main-old
Antonio De Lucreziis 3 years ago
parent a53ee26e78
commit 1ccb71d3a1

@ -7,4 +7,6 @@ GIT_URL=https://git.phc.dm.unipi.it
CHAT_URL=https://chat.phc.dm.unipi.it CHAT_URL=https://chat.phc.dm.unipi.it
# Other # Other
EMAIL=macchinisti.phc@gmail.com EMAIL=macchinisti@lists.dm.unipi.it
USER_PAGES_BASE_URL=https://poisson.phc.dm.unipi.it/~

3
.gitignore vendored

@ -3,3 +3,6 @@
# Miscellaneous # Miscellaneous
tags tags
# Local files
*.local*

@ -23,6 +23,8 @@ Un comando comodo in fase di development che usa [`entr`](https://github.com/era
```bash shell ```bash shell
$ find . -type f -name '*.go' | entr -r go run . $ find . -type f -name '*.go' | entr -r go run .
# Or also...
$ fd -e go | entr -r go run .
``` ```
### Environment Variables ### Environment Variables

@ -16,6 +16,8 @@ var GitUrl string
var ChatUrl string var ChatUrl string
var Email string var Email string
var UserPagesBaseUrl string
func loadEnv(target *string, name, defaultValue string) { func loadEnv(target *string, name, defaultValue string) {
value := os.Getenv(name) value := os.Getenv(name)
if len(strings.TrimSpace(value)) == 0 { if len(strings.TrimSpace(value)) == 0 {
@ -33,17 +35,23 @@ func Load() {
loadEnv(&Mode, "MODE", "production") loadEnv(&Mode, "MODE", "production")
loadEnv(&Host, "HOST", "localhost:8080") loadEnv(&Host, "HOST", "localhost:8080")
loadEnv(&GitUrl, "GIT_URL", "https://git.example.org") loadEnv(&GitUrl, "GIT_URL", "https://git.example.org")
loadEnv(&ChatUrl, "CHAT_URL", "https://chat.example.org") loadEnv(&ChatUrl, "CHAT_URL", "https://chat.example.org")
loadEnv(&Email, "EMAIL", "mail@example.org") loadEnv(&Email, "EMAIL", "mail@example.org")
loadEnv(&UserPagesBaseUrl, "USER_PAGES_BASE_URL", "https://poisson.phc.dm.unipi.it/~")
} }
func Object() util.H { func Object() util.H {
return util.H{ return util.H{
"Mode": Mode, "Mode": Mode,
"Host": Host, "Host": Host,
"GitUrl": GitUrl, "GitUrl": GitUrl,
"ChatUrl": ChatUrl, "ChatUrl": ChatUrl,
"Email": Email, "Email": Email,
"UserPagesBaseUrl": UserPagesBaseUrl,
} }
} }

@ -1,6 +1,7 @@
package main package main
import ( import (
"encoding/json"
"html/template" "html/template"
"log" "log"
"net/http" "net/http"
@ -40,7 +41,6 @@ func main() {
actuallyStaticRoutes := map[string]string{ actuallyStaticRoutes := map[string]string{
"/": "home.html", "/": "home.html",
"/link": "link.html", "/link": "link.html",
"/utenti": "utenti.html",
"/login": "login.html", "/login": "login.html",
} }
@ -55,6 +55,34 @@ func main() {
}) })
} }
r.Get("/api/utenti", func(w http.ResponseWriter, r *http.Request) {
utenti, err := GetUtenti()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := json.NewEncoder(w).Encode(utenti); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
r.Get("/utenti", func(w http.ResponseWriter, r *http.Request) {
utenti, err := GetUtenti()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := renderer.Render(w, "utenti.html", util.H{
"Utenti": utenti,
}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
r.Get("/appunti", func(w http.ResponseWriter, r *http.Request) { r.Get("/appunti", func(w http.ResponseWriter, r *http.Request) {
searchQuery := "" searchQuery := ""

@ -0,0 +1,3 @@
// window.addEventListener('DOMContentLoaded', () => {
// })

@ -475,6 +475,31 @@ button.icon {
width: 2rem; width: 2rem;
} }
select {
font-family: var(--font-sf);
font-weight: var(--font-weight-medium);
font-size: 16px;
/* gray variant #b3b3b3 */
border: 1px solid var(--bg-darker-3);
/* gray variant #bfbfbf */
background: var(--bg-darker-2);
/* gray variant #333333 */
color: var(--fg);
height: 2rem;
border-radius: 4px;
padding: 0 0.25rem;
transition: all 100ms ease-in-out;
box-shadow: 0 4px 8px 0 #00000022;
cursor: pointer;
}
/* Text Fields */ /* Text Fields */
input[type=text], input[type=password] { input[type=text], input[type=password] {
@ -511,9 +536,35 @@ input[type=password] {
border-radius: 4px; border-radius: 4px;
box-shadow: 0 0 8px 0 #00000022; box-shadow: 0 0 8px 0 #00000022;
border: 1px solid var(--bg-darker-3);
background: var(--bg-darker-2);
color: var(--fg);
} }
.compound > button, .compound > .button, .compound > input { .compound > .divider {
height: 2rem;
width: 1px;
background: var(--bg-darker-3);
}
.compound .icon {
width: 2.5rem;
display: flex;
align-items: center;
justify-content: center;
}
.compound > select {
background: none;
margin-right: 0.25rem;
}
.compound > button, .compound > .button, .compound > input, .compound > select {
border: none;
box-shadow: none; box-shadow: none;
} }
@ -633,8 +684,23 @@ form .field-set input {
padding-top: calc(100vh - 6rem); padding-top: calc(100vh - 6rem);
} }
.page-utenti .user-list {
display: flex;
flex-direction: column;
width: 100%;
}
.page-utenti .user-item {
flex-direction: row;
width: 100%;
}
.search { .search {
margin: 2rem 0; margin: 2rem 0;
display: flex;
flex-direction: row;
gap: 1rem;
} }
.search input[type=text] { .search input[type=text] {

@ -3,6 +3,7 @@ package templates
import ( import (
"html/template" "html/template"
"io" "io"
"log"
"path" "path"
"strings" "strings"
@ -10,20 +11,23 @@ import (
"git.phc.dm.unipi.it/phc/website/util" "git.phc.dm.unipi.it/phc/website/util"
) )
// CachedTemplate holds a reference to the list of patterns to preload and a reference to the already parsed template
type CachedTemplate struct { type CachedTemplate struct {
LoadPatterns []string PreLoadTemplatePatterns []string
cachedTmpl *template.Template cachedTmpl *template.Template
} }
// NewCacheTemplate creates a new template from a list of patterns a loads it
func NewCacheTemplate(loadPatterns ...string) *CachedTemplate { func NewCacheTemplate(loadPatterns ...string) *CachedTemplate {
cachedTemplate := &CachedTemplate{ cachedTemplate := &CachedTemplate{
LoadPatterns: loadPatterns, PreLoadTemplatePatterns: loadPatterns,
cachedTmpl: nil, cachedTmpl: nil,
} }
template.Must(cachedTemplate.Load(template.New(""))) template.Must(cachedTemplate.Load(template.New("")))
return cachedTemplate return cachedTemplate
} }
// Load returns the cached template or loads it from disk using the list of preload (glob) patterns.
func (ct *CachedTemplate) Load(t *template.Template) (*template.Template, error) { func (ct *CachedTemplate) Load(t *template.Template) (*template.Template, error) {
if ct.cachedTmpl != nil { if ct.cachedTmpl != nil {
return ct.cachedTmpl, nil return ct.cachedTmpl, nil
@ -31,7 +35,7 @@ func (ct *CachedTemplate) Load(t *template.Template) (*template.Template, error)
ct.cachedTmpl = t ct.cachedTmpl = t
for _, pattern := range ct.LoadPatterns { for _, pattern := range ct.PreLoadTemplatePatterns {
var err error var err error
ct.cachedTmpl, err = ct.cachedTmpl.ParseGlob(pattern) ct.cachedTmpl, err = ct.cachedTmpl.ParseGlob(pattern)
if err != nil { if err != nil {
@ -42,38 +46,43 @@ func (ct *CachedTemplate) Load(t *template.Template) (*template.Template, error)
return ct.cachedTmpl, nil return ct.cachedTmpl, nil
} }
// Reload the pattern from disk (used for developing)
func (ct *CachedTemplate) Reload() { func (ct *CachedTemplate) Reload() {
ct.cachedTmpl = nil ct.cachedTmpl = nil
template.Must(ct.Load(nil)) template.Must(ct.Load(nil))
} }
// Template returns the cached template
func (ct *CachedTemplate) Template() *template.Template { func (ct *CachedTemplate) Template() *template.Template {
log.Printf("preloadPatterns: %v", ct.PreLoadTemplatePatterns)
return ct.cachedTmpl return ct.cachedTmpl
} }
// Renderer holds cached templates for rendering // TODO: Add a render function to CachedTemplate instead of returning the template reference. In this way the Template Renderer would not have a "direct" dependency on "html/template"...
type Renderer struct {
rootPath string // TemplateRenderer holds cached templates for rendering
loadPatterns []string type TemplateRenderer struct {
viewsDir string
preloadTemplatePatterns []string
templateCache map[string]*CachedTemplate templateCache map[string]*CachedTemplate
} }
// NewRenderer constructs a template renderer with a base file // NewRenderer constructs a template renderer with a base file
func NewRenderer(rootPath string, loadPatterns ...string) *Renderer { func NewRenderer(rootPath string, loadPatterns ...string) *TemplateRenderer {
return &Renderer{ return &TemplateRenderer{
rootPath, rootPath,
loadPatterns, loadPatterns,
map[string]*CachedTemplate{}, map[string]*CachedTemplate{},
} }
} }
func (r *Renderer) Load(name string) *CachedTemplate { func (r *TemplateRenderer) Load(name string) *CachedTemplate {
cachedTemplate, present := r.templateCache[name] cachedTemplate, present := r.templateCache[name]
if !present { if !present {
loaders := []string{} loaders := []string{}
loaders = append(loaders, r.loadPatterns...) loaders = append(loaders, r.preloadTemplatePatterns...)
loaders = append(loaders, path.Join(r.rootPath, name)) loaders = append(loaders, path.Join(r.viewsDir, name))
cachedTemplate = NewCacheTemplate(loaders...) cachedTemplate = NewCacheTemplate(loaders...)
r.templateCache[name] = cachedTemplate r.templateCache[name] = cachedTemplate
} }
@ -82,7 +91,7 @@ func (r *Renderer) Load(name string) *CachedTemplate {
} }
// Render the template, also injects "Page" and "Config" values in the template // Render the template, also injects "Page" and "Config" values in the template
func (r *Renderer) Render(w io.Writer, name string, data util.H) error { func (r *TemplateRenderer) Render(w io.Writer, name string, data util.H) error {
cachedTemplate := r.Load(name) cachedTemplate := r.Load(name)
if config.Mode == "development" { if config.Mode == "development" {

@ -0,0 +1,27 @@
package main
import (
"encoding/json"
"io/ioutil"
)
type UserInfo struct {
Uid string `string:"uid"`
Nome string `json:"nome"`
Cognome string `json:"cognome"`
}
func GetUtenti() ([]UserInfo, error) {
var users []UserInfo
usersJsonData, err := ioutil.ReadFile("./utenti-poisson-2022.local.json")
if err != nil {
return nil, err
}
if err := json.Unmarshal(usersJsonData, &users); err != nil {
return nil, err
}
return users, nil
}

@ -1,7 +1,9 @@
package util package util
// H is a shortcut for creating a json-like object
type H map[string]interface{} type H map[string]interface{}
// Apply is like Object.apply from JS and merges the given objects on top of target
func (target H) Apply(sources ...H) H { func (target H) Apply(sources ...H) H {
for _, source := range sources { for _, source := range sources {
for k, v := range source { for k, v := range source {

@ -12,6 +12,17 @@
Questa è la lista di tutti gli utenti con un account su Poisson. Scrivi nome, cognome o username di un utente per filtrare la lista in tempo reale. Altrimenti di base in cima compariranno gli utenti con più "follower". Questa è la lista di tutti gli utenti con un account su Poisson. Scrivi nome, cognome o username di un utente per filtrare la lista in tempo reale. Altrimenti di base in cima compariranno gli utenti con più "follower".
</p> </p>
<div class="search"> <div class="search">
<div class="compound padded">
<div class="icon">
<i class="fas fa-sort"></i>
</div>
<div class="divider"></div>
<select>
<option value="name">Cronologico</option>
<option value="name">Nome</option>
<option value="name">Cognome</option>
</select>
</div>
<div class="compound"> <div class="compound">
<input type="text" id="search-field" placeholder="Cerca..." autocomplete="off"> <input type="text" id="search-field" placeholder="Cerca..." autocomplete="off">
<button class="icon"> <button class="icon">
@ -19,12 +30,20 @@
</button> </button>
</div> </div>
</div> </div>
<div class="user-list"> <div class="user-list card-list">
<code>TODO: Lista work in progress</code> {{ $config := .Config }}
{{ range .Utenti }}
<div class="user-item card">
<a class="full-name" href="{{ $config.UserPagesBaseUrl }}{{ .Uid }}">{{ .Nome }} {{ .Cognome }}</a>
</div>
{{ end }}
</div> </div>
</section> </section>
<script>
</script> <!--
<script src="//unpkg.com/alpinejs" defer></script>
<script src="https://cdn.jsdelivr.net/npm/fuse.js/dist/fuse.js" defer></script>
<script type="module" src="/public/js/utenti.js"></script>
-->
{{end}} {{end}}

Loading…
Cancel
Save