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
# 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
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

@ -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,
"GitUrl": GitUrl,
"ChatUrl": ChatUrl,
"Email": Email,
"UserPagesBaseUrl": UserPagesBaseUrl,
}
}

@ -1,6 +1,7 @@
package main
import (
"encoding/json"
"html/template"
"log"
"net/http"
@ -40,7 +41,6 @@ func main() {
actuallyStaticRoutes := map[string]string{
"/": "home.html",
"/link": "link.html",
"/utenti": "utenti.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) {
searchQuery := ""

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

@ -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] {

@ -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
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,
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
// 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 {

@ -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}}

Loading…
Cancel
Save