Author page, load js

This commit is contained in:
Meutel 2017-08-04 18:57:15 +02:00
parent 3ab0198f21
commit cfdd62f125
7 changed files with 101 additions and 142 deletions

View File

@ -1,83 +1,9 @@
var app = new Vue({ var author = new Vue({
el: '#app', el: '#author',
data: { data: {
urlParams: {},
author: {},
tab: "books" tab: "books"
}, },
methods: { methods: {
urlParse: function() {
var match,
pl = /\+/g, // Regex for replacing addition symbol with a space
search = /([^&=]+)=?([^&]*)/g,
decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
query = window.location.search.substring(1);
while (match = search.exec(query))
this.urlParams[decode(match[1])] = decode(match[2]);
},
sendQuery: function(url, error, success) {
var xmh = new XMLHttpRequest();
var v;
xmh.onreadystatechange = function() {
v = xmh.responseText;
if (xmh.readyState === 4 && xmh.status === 200) {
var res;
try {
res = JSON.parse(v);
} catch (err) {
if (null !== error)
error(err.name, err.message);
}
if (null !== success)
success(res);
} else if (xmh.readyState === 4) {
if (null !== error)
error(xmh.status, v);
}
};
xmh.open('GET', url, true);
xmh.send(null);
},
stdError: function(code, resp) {
console.log('ERROR ' + code + ': ' + resp);
},
authorSuccess: function(resp) {
this.author = resp;
document.title = this.author.name +' | Bouquins';
this.author.series=[];
this.author.authors=[];
if (this.author.books) {
var series = [];
var authors = [];
for (var i=0;i<this.author.books.length;i++) {
var book = this.author.books[i];
if (book.series)
series.push(book.series);
if (book.authors)
authors.push.apply(authors, book.authors);
}
if (series.length > 0) {
series.sort();
this.author.series = [series[0]];
for (var i=1;i<series.length;i++) {
if (series[i-1].id !== series[i].id)
this.author.series.push(series[i]);
}
}
if (authors.length > 0) {
authors.sort();
for (var i=0;i<authors.length;i++) {
if ((this.author.authors.length == 0
|| this.author.authors[this.author.authors.length] != authors[i])
&& authors[i].id != this.author.id)
this.author.authors.push(authors[i]);
}
}
}
},
showBooks: function() { showBooks: function() {
this.tab = "books"; this.tab = "books";
}, },
@ -86,16 +12,6 @@ var app = new Vue({
}, },
showSeries: function() { showSeries: function() {
this.tab = "series"; this.tab = "series";
},
loadAuthor: function() {
if (this.urlParams.id)
this.sendQuery('cgi-bin/bouquins/authors/' + this.urlParams.id, this.stdError, this.authorSuccess);
} }
},
created: function() {
this.urlParse();
},
mounted: function() {
this.loadAuthor();
} }
}) })

View File

@ -94,7 +94,9 @@ type BookAdv struct {
type AuthorFull struct { type AuthorFull struct {
Author Author
Books []*BookAdv `json:"books,omitempty"` Books []*Book `json:"books,omitempty"`
Series []*Series `json:"series,omitempty"`
CoAuthors []*Author `json:"coauthors,omitempty"`
} }
type BookFull struct { type BookFull struct {
@ -123,13 +125,18 @@ type SeriesFull struct {
} }
type BouquinsModel struct { type BouquinsModel struct {
Title string `json:"title,omitempty"` Title string `json:"title,omitempty"`
PageJs string `json:"-"`
} }
// Constructor BouquinsModel // Constructor BouquinsModel
func NewBouquinsModel(title string) *BouquinsModel { func NewBouquinsModel(title string) *BouquinsModel {
return NewBouquinsModelJs(title, "")
}
func NewBouquinsModelJs(title, js string) *BouquinsModel {
return &BouquinsModel{ return &BouquinsModel{
title, title,
js,
} }
} }
@ -263,7 +270,7 @@ func (app *Bouquins) AuthorsPage(res http.ResponseWriter, req *http.Request) {
log.Fatalln(err) log.Fatalln(err)
} }
app.render(res, TPL_AUTHORS, &AuthorModel{ app.render(res, TPL_AUTHORS, &AuthorModel{
*NewBouquinsModel(author.Name), *NewBouquinsModelJs(author.Name, "author.js"),
author, author,
}) })
} }

View File

@ -95,52 +95,55 @@ const (
LEFT OUTER JOIN series ON series.id = books_series_link.series LEFT OUTER JOIN series ON series.id = books_series_link.series
LEFT OUTER JOIN books_authors_link ON books.id = books_authors_link.book LEFT OUTER JOIN books_authors_link ON books.id = books_authors_link.book
WHERE books_authors_link.author = ? ORDER BY id` WHERE books_authors_link.author = ? ORDER BY id`
STMT_AUTHORS_AUTHORS = `SELECT authors.id, authors.name, books_authors_link.book as book STMT_AUTHOR_AUTHORS = `SELECT DISTINCT authors.id, authors.name
FROM authors, books_authors_link WHERE books_authors_link.author = authors.id FROM authors, books_authors_link WHERE books_authors_link.author = authors.id
AND books_authors_link.book IN ( SELECT books.id FROM books LEFT OUTER JOIN books_authors_link AND books_authors_link.book IN ( SELECT books.id FROM books LEFT OUTER JOIN books_authors_link
ON books.id = books_authors_link.book WHERE books_authors_link.author = ? ORDER BY books.id)` ON books.id = books_authors_link.book WHERE books_authors_link.author = ? ORDER BY books.id)
AND authors.id != ? ORDER BY authors.id`
STMT_AUTHOR = "SELECT name FROM authors WHERE id = ?" STMT_AUTHOR = "SELECT name FROM authors WHERE id = ?"
DEF_LIM = 10 DEF_LIM = 10
BOOKS QueryType = iota BOOKS QueryType = iota
BOOKS_TAGS QueryType = iota BOOKS_TAGS QueryType = iota
BOOKS_AUTHORS QueryType = iota BOOKS_AUTHORS QueryType = iota
BOOK QueryType = iota BOOK QueryType = iota
BOOK_TAGS QueryType = iota BOOK_TAGS QueryType = iota
BOOK_DATA QueryType = iota BOOK_DATA QueryType = iota
BOOK_AUTHORS QueryType = iota BOOK_AUTHORS QueryType = iota
BOOKS_COUNT QueryType = iota BOOKS_COUNT QueryType = iota
SERIES QueryType = iota SERIES QueryType = iota
SERIES_AUTHORS QueryType = iota SERIES_AUTHORS QueryType = iota
SERIES_BOOKS QueryType = iota SERIES_BOOKS QueryType = iota
AUTHOR QueryType = iota AUTHOR QueryType = iota
AUTHOR_BOOKS QueryType = iota AUTHOR_BOOKS QueryType = iota
AUTHOR_COAUTHORS QueryType = iota
) )
var QUERIES = map[Query]string{ var QUERIES = map[Query]string{
Query{BOOKS, true, true}: STMT_BOOKS_TITLE_DESC, Query{BOOKS, true, true}: STMT_BOOKS_TITLE_DESC,
Query{BOOKS, true, false}: STMT_BOOKS_TITLE_ASC, Query{BOOKS, true, false}: STMT_BOOKS_TITLE_ASC,
Query{BOOKS, false, true}: STMT_BOOKS_ID_DESC, Query{BOOKS, false, true}: STMT_BOOKS_ID_DESC,
Query{BOOKS, false, false}: STMT_BOOKS_ID_ASC, Query{BOOKS, false, false}: STMT_BOOKS_ID_ASC,
Query{BOOKS_TAGS, true, true}: STMT_BOOKS_TAGS_TITLE_DESC, Query{BOOKS_TAGS, true, true}: STMT_BOOKS_TAGS_TITLE_DESC,
Query{BOOKS_TAGS, true, false}: STMT_BOOKS_TAGS_TITLE_ASC, Query{BOOKS_TAGS, true, false}: STMT_BOOKS_TAGS_TITLE_ASC,
Query{BOOKS_TAGS, false, true}: STMT_BOOKS_TAGS_ID_DESC, Query{BOOKS_TAGS, false, true}: STMT_BOOKS_TAGS_ID_DESC,
Query{BOOKS_TAGS, false, false}: STMT_BOOKS_TAGS_ID_ASC, Query{BOOKS_TAGS, false, false}: STMT_BOOKS_TAGS_ID_ASC,
Query{BOOKS_AUTHORS, true, true}: STMT_BOOKS_AUTHORS_TITLE_DESC, Query{BOOKS_AUTHORS, true, true}: STMT_BOOKS_AUTHORS_TITLE_DESC,
Query{BOOKS_AUTHORS, true, false}: STMT_BOOKS_AUTHORS_TITLE_ASC, Query{BOOKS_AUTHORS, true, false}: STMT_BOOKS_AUTHORS_TITLE_ASC,
Query{BOOKS_AUTHORS, false, true}: STMT_BOOKS_AUTHORS_ID_DESC, Query{BOOKS_AUTHORS, false, true}: STMT_BOOKS_AUTHORS_ID_DESC,
Query{BOOKS_AUTHORS, false, false}: STMT_BOOKS_AUTHORS_ID_ASC, Query{BOOKS_AUTHORS, false, false}: STMT_BOOKS_AUTHORS_ID_ASC,
Query{BOOK, false, false}: STMT_BOOK, Query{BOOK, false, false}: STMT_BOOK,
Query{BOOK_TAGS, false, false}: STMT_BOOK_TAGS, Query{BOOK_TAGS, false, false}: STMT_BOOK_TAGS,
Query{BOOK_DATA, false, false}: STMT_BOOK_DATA, Query{BOOK_DATA, false, false}: STMT_BOOK_DATA,
Query{BOOK_AUTHORS, false, false}: STMT_BOOK_AUTHORS, Query{BOOK_AUTHORS, false, false}: STMT_BOOK_AUTHORS,
Query{BOOKS_COUNT, false, false}: STMT_BOOKS_COUNT, Query{BOOKS_COUNT, false, false}: STMT_BOOKS_COUNT,
Query{SERIES, false, false}: STMT_SERIE, Query{SERIES, false, false}: STMT_SERIE,
Query{SERIES_AUTHORS, false, false}: STMT_SERIE_AUTHORS, Query{SERIES_AUTHORS, false, false}: STMT_SERIE_AUTHORS,
Query{SERIES_BOOKS, false, false}: STMT_SERIE_BOOKS, Query{SERIES_BOOKS, false, false}: STMT_SERIE_BOOKS,
Query{AUTHOR, false, false}: STMT_AUTHOR, Query{AUTHOR, false, false}: STMT_AUTHOR,
Query{AUTHOR_BOOKS, false, false}: STMT_AUTHOR_BOOKS, Query{AUTHOR_BOOKS, false, false}: STMT_AUTHOR_BOOKS,
Query{AUTHOR_COAUTHORS, false, false}: STMT_AUTHOR_AUTHORS,
} }
var STMTS = make(map[Query]*sql.Stmt) var STMTS = make(map[Query]*sql.Stmt)

View File

@ -1,6 +1,8 @@
package bouquins package bouquins
import "database/sql" import (
"database/sql"
)
// SUB QUERIES // // SUB QUERIES //
@ -14,15 +16,16 @@ func (app *Bouquins) queryAuthorBooks(author *AuthorFull) error {
return err return err
} }
defer rows.Close() defer rows.Close()
series := make(map[int64]*Series, 0)
for rows.Next() { for rows.Next() {
book := new(BookAdv) book := new(Book)
var seriesId sql.NullInt64 var seriesId sql.NullInt64
var seriesName sql.NullString var seriesName sql.NullString
if err = rows.Scan(&book.Id, &book.Title, &book.SeriesIndex, &seriesName, &seriesId); err != nil { if err = rows.Scan(&book.Id, &book.Title, &book.SeriesIndex, &seriesName, &seriesId); err != nil {
return err return err
} }
if seriesId.Valid && seriesName.Valid { if seriesId.Valid && seriesName.Valid {
book.Series = &Series{ series[seriesId.Int64] = &Series{
seriesId.Int64, seriesId.Int64,
seriesName.String, seriesName.String,
} }
@ -32,11 +35,33 @@ func (app *Bouquins) queryAuthorBooks(author *AuthorFull) error {
if err := rows.Err(); err != nil { if err := rows.Err(); err != nil {
return err return err
} }
author.Series = make([]*Series, 0, len(series))
for _, s := range series {
author.Series = append(author.Series, s)
}
return nil return nil
} }
func (app *Bouquins) queryAuthorAuthors(author *AuthorFull) error { func (app *Bouquins) queryAuthorAuthors(author *AuthorFull) error {
// TODO stmt, err := app.ps(AUTHOR_COAUTHORS)
if err != nil {
return err
}
rows, err := stmt.Query(author.Id, author.Id)
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
coauthor := new(Author)
if err = rows.Scan(&coauthor.Id, &coauthor.Name); err != nil {
return err
}
author.CoAuthors = append(author.CoAuthors, coauthor)
}
if err := rows.Err(); err != nil {
return err
}
return nil return nil
} }

View File

@ -1,5 +1,5 @@
{{ template "header.html" . }} {{ template "header.html" . }}
<div class="container" id="app"> <div class="container" id="author">
{{ if .Id }} {{ if .Id }}
<div class="page-header"> <div class="page-header">
<h1> <h1>
@ -9,12 +9,12 @@
</div> </div>
<ul class="nav nav-pills"> <ul class="nav nav-pills">
<li role="presentation" :class="{ active: tab == 'books' }"><a href="#" @click="showBooks">Livres</a></li> <li role="presentation" :class="{ active: tab == 'books' }"><a href="#" @click="showBooks">Livres</a></li>
{{/* if gt (len .Series) 0 */}} {{ if gt (len .Series) 0 }}
<li role="presentation" :class="{ active: tab == 'series' }"><a href="#" @click="showSeries">Series</a></li> <li role="presentation" :class="{ active: tab == 'series' }"><a href="#" @click="showSeries">Series</a></li>
{{/* end */}} {{ end }}
{{/* if gt (len .Authors) 0 */}} {{ if gt (len .CoAuthors) 0 }}
<li role="presentation" :class="{ active: tab == 'authors' }"><a href="#" @click="showAuthors">Co-auteurs</a></li> <li role="presentation" :class="{ active: tab == 'authors' }"><a href="#" @click="showAuthors">Co-auteurs</a></li>
{{/* end */}} {{ end }}
</ul> </ul>
<div class="panel panel-default" :class="{ hidden: tab != 'books' }"> <div class="panel panel-default" :class="{ hidden: tab != 'books' }">
<div class="panel-body"> <div class="panel-body">
@ -27,33 +27,32 @@
{{ end }} {{ end }}
</div> </div>
</div> </div>
{{ if gt (len .Series) 0 }}
<div class="panel panel-default" :class="{ hidden: tab != 'series' }"> <div class="panel panel-default" :class="{ hidden: tab != 'series' }">
<div class="panel-body"> <div class="panel-body">
{{ range .Books }} {{ range .Series }}
{{/* FIXME unicity */}}
{{ with .Series }}
<ul class="list-unstyled"> <ul class="list-unstyled">
<li><span class="glyphicon glyphicon-list"></span> <li><span class="glyphicon glyphicon-list"></span>
<a href="/series/{{ .Id }}">{{ .Name }}</a> <a href="/series/{{ .Id }}">{{ .Name }}</a>
</li> </li>
</ul> </ul>
{{ end }} {{ end }}
{{ end }}
</div> </div>
</div> </div>
{{ end }}
{{ if gt (len .CoAuthors) 0 }}
<div class="panel panel-default" :class="{ hidden: tab != 'authors' }"> <div class="panel panel-default" :class="{ hidden: tab != 'authors' }">
<div class="panel-body"> <div class="panel-body">
{{ range .Books }} {{ range .CoAuthors }}
{{ range .Authors }}
<ul class="list-unstyled"> <ul class="list-unstyled">
<li> <span class="glyphicon glyphicon-user"></span> <li> <span class="glyphicon glyphicon-user"></span>
<a href="/authors/{{ .Id }}">{{ .Name }}</a> <a href="/authors/{{ .Id }}">{{ .Name }}</a>
</li> </li>
</ul> </ul>
{{ end }} {{ end }}
{{ end }}
</div> </div>
</div> </div>
{{ end }}
{{ else }} {{ else }}
<div class="alert alert-danger" role="alert">Aucun auteur sélectionné</div> <div class="alert alert-danger" role="alert">Aucun auteur sélectionné</div>
{{ end }} {{ end }}

View File

@ -1,3 +1,6 @@
<script src="/js/vue.min.js"></script> <script src="/js/vue.min.js"></script>
{{ if .PageJs }}
<script src="/js/{{ .PageJs }}"></script>
{{ end }}
</body> </body>
</html> </html>

View File

@ -5,6 +5,12 @@
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="stylesheet" href="/css/bootstrap.min.css"> <link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="preload" href="/js/vue.min.js" as="script">
<link rel="prefetch" href="/js/vue.min.js">
{{ if .PageJs }}
<link rel="preload" href="/js/{{ .PageJs }}" as="script">
<link rel="prefetch" href="/js/{{ .PageJs }}">
{{ end }}
</head> </head>
<body> <body>
<nav class="navbar navbar-inverse" id="nav"> <nav class="navbar navbar-inverse" id="nav">