forked from phc/website
Pagina utenti caricata da json
This commit is contained in:
@@ -7,4 +7,6 @@ GIT_URL=https://git.phc.dm.unipi.it
|
||||
CHAT_URL=https://chat.phc.dm.unipi.it
|
||||
|
||||
# Other
|
||||
EMAIL=macchinisti.phc@gmail.com
|
||||
EMAIL=macchinisti@lists.dm.unipi.it
|
||||
|
||||
USER_PAGES_BASE_URL=https://poisson.phc.dm.unipi.it/~
|
||||
|
||||
@@ -3,3 +3,6 @@
|
||||
|
||||
# Miscellaneous
|
||||
tags
|
||||
|
||||
# Local files
|
||||
*.local*
|
||||
|
||||
@@ -23,6 +23,8 @@ Un comando comodo in fase di development che usa [`entr`](https://github.com/era
|
||||
|
||||
```bash shell
|
||||
$ find . -type f -name '*.go' | entr -r go run .
|
||||
# Or also...
|
||||
$ fd -e go | entr -r go run .
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
+10
-2
@@ -16,6 +16,8 @@ var GitUrl string
|
||||
var ChatUrl string
|
||||
var Email string
|
||||
|
||||
var UserPagesBaseUrl string
|
||||
|
||||
func loadEnv(target *string, name, defaultValue string) {
|
||||
value := os.Getenv(name)
|
||||
if len(strings.TrimSpace(value)) == 0 {
|
||||
@@ -33,17 +35,23 @@ func Load() {
|
||||
|
||||
loadEnv(&Mode, "MODE", "production")
|
||||
loadEnv(&Host, "HOST", "localhost:8080")
|
||||
|
||||
loadEnv(&GitUrl, "GIT_URL", "https://git.example.org")
|
||||
loadEnv(&ChatUrl, "CHAT_URL", "https://chat.example.org")
|
||||
loadEnv(&Email, "EMAIL", "mail@example.org")
|
||||
|
||||
loadEnv(&UserPagesBaseUrl, "USER_PAGES_BASE_URL", "https://poisson.phc.dm.unipi.it/~")
|
||||
}
|
||||
|
||||
func Object() util.H {
|
||||
return util.H{
|
||||
"Mode": Mode,
|
||||
"Host": Host,
|
||||
"Mode": Mode,
|
||||
"Host": Host,
|
||||
|
||||
"GitUrl": GitUrl,
|
||||
"ChatUrl": ChatUrl,
|
||||
"Email": Email,
|
||||
|
||||
"UserPagesBaseUrl": UserPagesBaseUrl,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
@@ -38,10 +39,9 @@ func main() {
|
||||
// Routes
|
||||
|
||||
actuallyStaticRoutes := map[string]string{
|
||||
"/": "home.html",
|
||||
"/link": "link.html",
|
||||
"/utenti": "utenti.html",
|
||||
"/login": "login.html",
|
||||
"/": "home.html",
|
||||
"/link": "link.html",
|
||||
"/login": "login.html",
|
||||
}
|
||||
|
||||
for route, view := range actuallyStaticRoutes {
|
||||
@@ -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) {
|
||||
searchQuery := ""
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// window.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
// })
|
||||
+67
-1
@@ -475,6 +475,31 @@ button.icon {
|
||||
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 */
|
||||
|
||||
input[type=text], input[type=password] {
|
||||
@@ -511,9 +536,35 @@ input[type=password] {
|
||||
|
||||
border-radius: 4px;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -633,8 +684,23 @@ form .field-set input {
|
||||
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 {
|
||||
margin: 2rem 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.search input[type=text] {
|
||||
|
||||
+25
-16
@@ -3,6 +3,7 @@ package templates
|
||||
import (
|
||||
"html/template"
|
||||
"io"
|
||||
"log"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
@@ -10,20 +11,23 @@ import (
|
||||
"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 {
|
||||
LoadPatterns []string
|
||||
cachedTmpl *template.Template
|
||||
PreLoadTemplatePatterns []string
|
||||
cachedTmpl *template.Template
|
||||
}
|
||||
|
||||
// NewCacheTemplate creates a new template from a list of patterns a loads it
|
||||
func NewCacheTemplate(loadPatterns ...string) *CachedTemplate {
|
||||
cachedTemplate := &CachedTemplate{
|
||||
LoadPatterns: loadPatterns,
|
||||
cachedTmpl: nil,
|
||||
PreLoadTemplatePatterns: loadPatterns,
|
||||
cachedTmpl: nil,
|
||||
}
|
||||
template.Must(cachedTemplate.Load(template.New("")))
|
||||
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) {
|
||||
if ct.cachedTmpl != nil {
|
||||
return ct.cachedTmpl, nil
|
||||
@@ -31,7 +35,7 @@ func (ct *CachedTemplate) Load(t *template.Template) (*template.Template, error)
|
||||
|
||||
ct.cachedTmpl = t
|
||||
|
||||
for _, pattern := range ct.LoadPatterns {
|
||||
for _, pattern := range ct.PreLoadTemplatePatterns {
|
||||
var err error
|
||||
ct.cachedTmpl, err = ct.cachedTmpl.ParseGlob(pattern)
|
||||
if err != nil {
|
||||
@@ -42,38 +46,43 @@ func (ct *CachedTemplate) Load(t *template.Template) (*template.Template, error)
|
||||
return ct.cachedTmpl, nil
|
||||
}
|
||||
|
||||
// Reload the pattern from disk (used for developing)
|
||||
func (ct *CachedTemplate) Reload() {
|
||||
ct.cachedTmpl = nil
|
||||
template.Must(ct.Load(nil))
|
||||
}
|
||||
|
||||
// Template returns the cached template
|
||||
func (ct *CachedTemplate) Template() *template.Template {
|
||||
log.Printf("preloadPatterns: %v", ct.PreLoadTemplatePatterns)
|
||||
return ct.cachedTmpl
|
||||
}
|
||||
|
||||
// Renderer holds cached templates for rendering
|
||||
type Renderer struct {
|
||||
rootPath string
|
||||
loadPatterns []string
|
||||
templateCache map[string]*CachedTemplate
|
||||
// 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"...
|
||||
|
||||
// TemplateRenderer holds cached templates for rendering
|
||||
type TemplateRenderer struct {
|
||||
viewsDir string
|
||||
preloadTemplatePatterns []string
|
||||
templateCache map[string]*CachedTemplate
|
||||
}
|
||||
|
||||
// NewRenderer constructs a template renderer with a base file
|
||||
func NewRenderer(rootPath string, loadPatterns ...string) *Renderer {
|
||||
return &Renderer{
|
||||
func NewRenderer(rootPath string, loadPatterns ...string) *TemplateRenderer {
|
||||
return &TemplateRenderer{
|
||||
rootPath,
|
||||
loadPatterns,
|
||||
map[string]*CachedTemplate{},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Renderer) Load(name string) *CachedTemplate {
|
||||
func (r *TemplateRenderer) Load(name string) *CachedTemplate {
|
||||
cachedTemplate, present := r.templateCache[name]
|
||||
|
||||
if !present {
|
||||
loaders := []string{}
|
||||
loaders = append(loaders, r.loadPatterns...)
|
||||
loaders = append(loaders, path.Join(r.rootPath, name))
|
||||
loaders = append(loaders, r.preloadTemplatePatterns...)
|
||||
loaders = append(loaders, path.Join(r.viewsDir, name))
|
||||
cachedTemplate = NewCacheTemplate(loaders...)
|
||||
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
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
// H is a shortcut for creating a json-like object
|
||||
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 {
|
||||
for _, source := range sources {
|
||||
for k, v := range source {
|
||||
|
||||
+23
-4
@@ -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".
|
||||
</p>
|
||||
<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">
|
||||
<input type="text" id="search-field" placeholder="Cerca..." autocomplete="off">
|
||||
<button class="icon">
|
||||
@@ -19,12 +30,20 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-list">
|
||||
<code>TODO: Lista work in progress</code>
|
||||
<div class="user-list card-list">
|
||||
{{ $config := .Config }}
|
||||
{{ range .Utenti }}
|
||||
<div class="user-item card">
|
||||
<a class="full-name" href="{{ $config.UserPagesBaseUrl }}{{ .Uid }}">{{ .Nome }} {{ .Cognome }}</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</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}}
|
||||
|
||||
Reference in New Issue
Block a user