diff --git a/assets/js/index.js b/assets/js/index.js index c21301e..0fa414e 100644 --- a/assets/js/index.js +++ b/assets/js/index.js @@ -33,6 +33,10 @@ Vue.component('result-cell', { }, cellContent: function(h) { switch (this.col.id) { + case 'name': + return this.link(h, 'user', this.item.name, this.authorUrl(this.item.id)); + case 'count': + return this.item.count; case 'title': return this.link(h, 'book', this.item.title, this.bookUrl(this.item.id)); case 'authors': @@ -52,6 +56,7 @@ Vue.component('result-cell', { } return ''; default: + console.log('ERROR unknown col: ' + this.col.id) return ''; } } @@ -82,7 +87,7 @@ var index = new Vue({ console.log("Series"); }, showAuthors: function() { - console.log("Authors"); + this.sendQuery('/authors/', this.stdError, this.loadResults); }, showBooks: function() { this.sendQuery('/books/', this.stdError, this.loadResults); @@ -105,8 +110,8 @@ var index = new Vue({ break; case 'authors': this.cols = [ - { name: 'Nom', sortId: 'name' }, - { name: 'Livre(s)' } + { id: 'name', name: 'Nom', sortable: true }, + { id:'count', name: 'Livre(s)' } ]; break; } diff --git a/bouquins/bouquins.go b/bouquins/bouquins.go index a6b7e89..fdf125d 100644 --- a/bouquins/bouquins.go +++ b/bouquins/bouquins.go @@ -3,6 +3,7 @@ package bouquins import ( "database/sql" "encoding/json" + "errors" "fmt" "html/template" "log" @@ -158,21 +159,35 @@ func NewIndexModel(title, js string, count int64) *IndexModel { } } +type ResultsModel struct { + Type string `json:"type,omitempty"` +} type BooksResultsModel struct { + ResultsModel Results []*BookAdv `json:"results,omitempty"` - Type string `json:"type,omitempty"` } func NewBooksResultsModel(books []*BookAdv) *BooksResultsModel { - return &BooksResultsModel{ - books, - "books", - } + return &BooksResultsModel{ResultsModel{"books"}, books} } -// Books []*BookAdv `json:"books,omitempty"` -// Series []*SeriesAdv `json:"series,omitempty"` -// Authors []*AuthorAdv `json:"authors,omitempty"` +type AuthorsResultsModel struct { + ResultsModel + Results []*AuthorAdv `json:"results,omitempty"` +} + +func NewAuthorsResultsModel(authors []*AuthorAdv) *AuthorsResultsModel { + return &AuthorsResultsModel{ResultsModel{"authors"}, authors} +} + +type SerieResultsModel struct { + ResultsModel + Results []*SeriesAdv `json:"results,omitempty"` +} + +func NewSerieResultsModel(series []*SeriesAdv) *SerieResultsModel { + return &SerieResultsModel{ResultsModel{"series"}, series} +} type BookModel struct { BouquinsModel @@ -189,11 +204,8 @@ type AuthorModel struct { *AuthorFull } -func (app *Bouquins) render(res http.ResponseWriter, tpl string, model interface{}) { - err := app.Template.ExecuteTemplate(res, tpl, model) - if err != nil { - log.Print(err) - } +func (app *Bouquins) render(res http.ResponseWriter, tpl string, model interface{}) error { + return app.Template.ExecuteTemplate(res, tpl, model) } func TemplatesFunc() *template.Template { tpl := template.New("") @@ -212,6 +224,14 @@ func TemplatesFunc() *template.Template { return tpl } +func writeJson(res http.ResponseWriter, model interface{}) error { + res.Header().Set("Content-Type", "application/json") + enc := json.NewEncoder(res) + return enc.Encode(model) +} +func isJson(req *http.Request) bool { + return req.Header.Get("Accept") == "application/json" +} func paramInt(name string, req *http.Request) int { val := req.URL.Query().Get(name) valInt, err := strconv.Atoi(val) @@ -254,10 +274,8 @@ func (app *Bouquins) IndexPage(res http.ResponseWriter, req *http.Request) { log.Print(err) } */ - if req.Header.Get("Accept") == "application/json" { - res.Header().Set("Content-Type", "application/json") - enc := json.NewEncoder(res) - err := enc.Encode(model) + if isJson(req) { + err := writeJson(res, model) if err != nil { log.Println(err) http.Error(res, err.Error(), 500) @@ -266,68 +284,83 @@ func (app *Bouquins) IndexPage(res http.ResponseWriter, req *http.Request) { app.render(res, TPL_INDEX, model) } } -func (app *Bouquins) BooksPage(res http.ResponseWriter, req *http.Request) { - if !strings.HasPrefix(req.URL.Path, URL_BOOKS) { - // FIXME 404 - log.Fatalln("Invalid URL") +func (app *Bouquins) BooksListPage(res http.ResponseWriter, req *http.Request) error { + if isJson(req) { + books, err := app.BooksAdv(10, 0, "", "") + if err != nil { + return err + } + return writeJson(res, NewBooksResultsModel(books)) } - idParam := req.URL.Path[len(URL_BOOKS):] - if len(idParam) == 0 { - // books list - if req.Header.Get("Accept") == "application/json" { - books, err := app.BooksAdv(10, 0, "", "") - if err != nil { - log.Println(err) - http.Error(res, err.Error(), 500) - } - model := NewBooksResultsModel(books) - // FIXME params - res.Header().Set("Content-Type", "application/json") - enc := json.NewEncoder(res) - err = enc.Encode(model) - if err != nil { - log.Println(err) - http.Error(res, err.Error(), 500) - } - } + return errors.New("Invalid mime") +} +func (app *Bouquins) BookPage(idParam string, res http.ResponseWriter, req *http.Request) error { + id, err := strconv.Atoi(idParam) + if err != nil { + return nil + } + book, err := app.BookFull(int64(id)) + if err != nil { + return nil + } + return app.render(res, TPL_BOOKS, &BookModel{*NewBouquinsModel(book.Title), book}) +} +func (app *Bouquins) BooksPage(res http.ResponseWriter, req *http.Request) { + var err error + var idParam = "" + if strings.HasPrefix(req.URL.Path, URL_BOOKS) { + idParam = req.URL.Path[len(URL_BOOKS):] } else { - // book page - id, err := strconv.Atoi(idParam) - if err != nil { - // FIXME 404 - log.Fatalln(err) - } - book, err := app.BookFull(int64(id)) - if err != nil { - // FIXME 500 - log.Fatalln(err) - } - model := &BookModel{ - *NewBouquinsModel(book.Title), - book, - } - app.render(res, TPL_BOOKS, model) + err = errors.New("Invalid URL") // FIXME 404 + } + if len(idParam) == 0 { + err = app.BooksListPage(res, req) + } else { + err = app.BookPage(idParam, res, req) + } + if err != nil { + log.Println(err) + http.Error(res, err.Error(), 500) } } -func (app *Bouquins) AuthorsPage(res http.ResponseWriter, req *http.Request) { - if !strings.HasPrefix(req.URL.Path, URL_AUTHORS) { - // FIXME 404 - log.Fatalln("Invalid URL") +func (app *Bouquins) AuthorsListPage(res http.ResponseWriter, req *http.Request) error { + if isJson(req) { + authors, err := app.AuthorsAdv(10, 0, "", "") + if err != nil { + return err + } + return writeJson(res, NewAuthorsResultsModel(authors)) } - id, err := strconv.Atoi(req.URL.Path[len(URL_AUTHORS):]) + return errors.New("Invalid mime") +} +func (app *Bouquins) AuthorPage(idParam string, res http.ResponseWriter, req *http.Request) error { + id, err := strconv.Atoi(idParam) if err != nil { - // FIXME 404 - log.Fatalln(err) + return err } author, err := app.AuthorFull(int64(id)) if err != nil { - // FIXME 500 - log.Fatalln(err) + return err + } + return app.render(res, TPL_AUTHORS, &AuthorModel{*NewBouquinsModelJs(author.Name, "author.js"), author}) +} +func (app *Bouquins) AuthorsPage(res http.ResponseWriter, req *http.Request) { + var err error + var idParam = "" + if strings.HasPrefix(req.URL.Path, URL_AUTHORS) { + idParam = req.URL.Path[len(URL_AUTHORS):] + } else { + err = errors.New("Invalid URL") // FIXME 404 + } + if len(idParam) == 0 { + err = app.AuthorsListPage(res, req) + } else { + err = app.AuthorPage(idParam, res, req) + } + if err != nil { + log.Println(err) + http.Error(res, err.Error(), 500) } - app.render(res, TPL_AUTHORS, &AuthorModel{ - *NewBouquinsModelJs(author.Name, "author.js"), - author, - }) } func (app *Bouquins) SeriesPage(res http.ResponseWriter, req *http.Request) { if !strings.HasPrefix(req.URL.Path, URL_SERIES) { diff --git a/bouquins/db.go b/bouquins/db.go index 5262b94..f47edc1 100644 --- a/bouquins/db.go +++ b/bouquins/db.go @@ -29,7 +29,7 @@ const ( STMT_SEARCH_TERM_SERIES = " series.sort like ? " STMT_AUTHORS0 = `SELECT authors.id, authors.name, count(book) as count FROM authors, books_authors_link - WHERE authors.id = books_authors_link.author GROUP BY author` + WHERE authors.id = books_authors_link.author GROUP BY author ` STMT_AUTHORS_SEARCH = "SELECT id, name FROM authors WHERE " STMT_SEARCH_TERM_AUTHOR = " sort like ? " @@ -115,6 +115,7 @@ const ( SERIES QueryType = iota SERIES_AUTHORS QueryType = iota SERIES_BOOKS QueryType = iota + AUTHORS QueryType = iota AUTHOR QueryType = iota AUTHOR_BOOKS QueryType = iota AUTHOR_COAUTHORS QueryType = iota @@ -141,6 +142,10 @@ var QUERIES = map[Query]string{ Query{SERIES, false, false}: STMT_SERIE, Query{SERIES_AUTHORS, false, false}: STMT_SERIE_AUTHORS, Query{SERIES_BOOKS, false, false}: STMT_SERIE_BOOKS, + Query{AUTHORS, true, true}: STMT_AUTHORS_NAME_DESC, + Query{AUTHORS, true, false}: STMT_AUTHORS_NAME_ASC, + Query{AUTHORS, false, true}: STMT_AUTHORS_ID_DESC, + Query{AUTHORS, false, false}: STMT_AUTHORS_ID_ASC, Query{AUTHOR, false, false}: STMT_AUTHOR, Query{AUTHOR_BOOKS, false, false}: STMT_AUTHOR_BOOKS, Query{AUTHOR_COAUTHORS, false, false}: STMT_AUTHOR_AUTHORS, @@ -149,9 +154,9 @@ var STMTS = make(map[Query]*sql.Stmt) type QueryType uint type Query struct { - Type QueryType - Title bool - Desc bool + Type QueryType + SortField bool // sort by name or title + Desc bool } // PREPARED STATEMENTS // @@ -173,7 +178,13 @@ func (app *Bouquins) PrepareAll() error { // prepared statement with sort on books func (app *Bouquins) psSortBooks(qt QueryType, sort, order string) (*sql.Stmt, error) { - q := Query{qt, sort == "title", order == "desc"} + return app.psSort("title", qt, sort, order) +} +func (app *Bouquins) psSortAuthors(qt QueryType, sort, order string) (*sql.Stmt, error) { + return app.psSort("name", qt, sort, order) +} +func (app *Bouquins) psSort(sortNameField string, qt QueryType, sort, order string) (*sql.Stmt, error) { + q := Query{qt, sort == sortNameField, order == "desc"} query := QUERIES[q] log.Println(query) stmt := STMTS[q] diff --git a/bouquins/dbauthors.go b/bouquins/dbauthors.go index fdee694..2d77bf4 100644 --- a/bouquins/dbauthors.go +++ b/bouquins/dbauthors.go @@ -6,6 +6,30 @@ import ( // SUB QUERIES // +func (app *Bouquins) queryAuthors(limit, offset int, sort, order string) ([]*AuthorAdv, error) { + authors := make([]*AuthorAdv, 0, limit) + stmt, err := app.psSortAuthors(AUTHORS, sort, order) + if err != nil { + return nil, err + } + rows, err := stmt.Query(limit, offset) + if err != nil { + return nil, err + } + defer rows.Close() + for rows.Next() { + author := new(AuthorAdv) + if err := rows.Scan(&author.Id, &author.Name, &author.Count); err != nil { + return nil, err + } + authors = append(authors, author) + } + if err := rows.Err(); err != nil { + return nil, err + } + return authors, nil +} + func (app *Bouquins) queryAuthorBooks(author *AuthorFull) error { stmt, err := app.ps(AUTHOR_BOOKS) if err != nil { @@ -81,6 +105,17 @@ func (app *Bouquins) queryAuthor(id int64) (*AuthorFull, error) { // DB LOADS // +func (app *Bouquins) AuthorsAdv(limit, offset int, sort, order string) ([]*AuthorAdv, error) { + if limit == 0 { + limit = DEF_LIM + } + authors, err := app.queryAuthors(limit, offset, sort, order) + if err != nil { + return nil, err + } + return authors, nil +} + func (app *Bouquins) AuthorFull(id int64) (*AuthorFull, error) { author, err := app.queryAuthor(id) if err != nil { diff --git a/bouquins/dbbooks.go b/bouquins/dbbooks.go index 98ee34d..134a6b8 100644 --- a/bouquins/dbbooks.go +++ b/bouquins/dbbooks.go @@ -228,12 +228,6 @@ func (app *Bouquins) BookCount() (int64, error) { err = row.Scan(&count) return count, err } -func (app *Bouquins) SeriesAdv(limit, offset int, sort, order string) ([]*SeriesAdv, error) { - panic("not implemented") -} -func (app *Bouquins) AuthorsAdv(limit, offset int, sort, order string) ([]*AuthorAdv, error) { - panic("not implemented") -} func (app *Bouquins) BookFull(id int64) (*BookFull, error) { book, err := app.queryBook(id) diff --git a/bouquins/dbseries.go b/bouquins/dbseries.go index 33c6d2d..85756a7 100644 --- a/bouquins/dbseries.go +++ b/bouquins/dbseries.go @@ -73,3 +73,7 @@ func (app *Bouquins) SeriesFull(id int64) (*SeriesFull, error) { } return series, nil } + +func (app *Bouquins) SeriesAdv(limit, offset int, sort, order string) ([]*SeriesAdv, error) { + panic("not implemented") +}