Voting is working
This commit is contained in:
parent
153b869a98
commit
96a53b6090
@ -15,21 +15,11 @@ func handleAdminGames(w http.ResponseWriter, req *http.Request, page *pageData)
|
||||
teamId := vars["id"]
|
||||
if teamId == "" {
|
||||
// Games List
|
||||
type gamePage struct {
|
||||
Game *Game
|
||||
Team *Team
|
||||
}
|
||||
type gamesPageData struct {
|
||||
Games []gamePage
|
||||
Teams []Team
|
||||
}
|
||||
gpd := new(gamesPageData)
|
||||
allGames := dbGetAllGames()
|
||||
for _, gm := range allGames {
|
||||
gamePage := new(gamePage)
|
||||
gamePage.Game = &gm
|
||||
gamePage.Team = dbGetTeam(gm.TeamId)
|
||||
gpd.Games = append(gpd.Games, *gamePage)
|
||||
}
|
||||
gpd.Teams = dbGetAllTeams()
|
||||
page.TemplateData = gpd
|
||||
page.SubTitle = "Games"
|
||||
page.show("admin-games.html", w)
|
||||
|
77
assets.go
77
assets.go
@ -192,35 +192,38 @@ var _escData = map[string]*_escFile{
|
||||
|
||||
"/assets/css/admin.css": {
|
||||
local: "assets/css/admin.css",
|
||||
size: 83,
|
||||
modtime: 1497482098,
|
||||
size: 121,
|
||||
modtime: 1498145831,
|
||||
compressed: `
|
||||
H4sIAAAAAAAA/0rJLNNLzs8rSc0rUajmUlDITSxKz8zTzUlNK7FSMDQ1KKiw5qrl4gIpS8ovKcnP1S0u
|
||||
SExORVYLEQephigGBAAA//8E7lAbUwAAAA==
|
||||
H4sIAAAAAAAA/0zKQQoCMQwAwHtekQ/sogcv62u6NdZAk5Q0FUH8u1Q9eB3mwvc1mwZp4BMQJXlhXSpd
|
||||
Y8Pj6dAeZ3gBzLZbhMnSW8r0f78+9y+zlDVuQ3ZNXD8zD+/mGzZjDfK53gEAAP//D/6XxHkAAAA=
|
||||
`,
|
||||
},
|
||||
|
||||
"/assets/css/gjvote.css": {
|
||||
local: "assets/css/gjvote.css",
|
||||
size: 2862,
|
||||
modtime: 1497542484,
|
||||
size: 3710,
|
||||
modtime: 1498849329,
|
||||
compressed: `
|
||||
H4sIAAAAAAAA/6xW22rkOBB9768QNAsJrIydSU8SB4Z9mOx/yFa1XUSWjCR3O7Pk3xdd7PY17MI0/WCX
|
||||
6nKqdKrKtW0E+edASIOS1oBVbXOSpekfr4fPw6FQ/MOflkoonZPj09PTa1SGusKZMsdLUippQVpv0zLO
|
||||
UVY5yU5t/7oZYnRcCFa+ey9JDYyDDpiYrlBSAWebk9Qfo2w7m5xRwDxGcoKGJN+hcU45mlawj5ygFCiB
|
||||
FkI574QUSnPQOcnanhglkJNjWZbhpKemZlxdnZUBS1Kv9a3tyZFzfrOmmnHsTE4eXVafh8OxAdl5NFfk
|
||||
tr7lNuT6mIb8W2XQopI50SCYxQt4r6x8r7TqJM/JMXvJnrNnJ75C8Y6Wqgvos1BXakqthPC5WtWV9SR0
|
||||
0nYaqHukrngoK49mpwpbZlI5lfetq55XOFsUeKyshd5SDqXSLKQolfTpXWu0QE3LSnDCq2btFIMPX3TW
|
||||
KrkFel42VhglOuulVrU5iYXVoc7xLbLGK2RzmR7IF6QxVQ/xiyQcKwNOZa1qZld9iq4iN0LQkV3R8yJd
|
||||
1yQMZST5mPFQse185zn6OAOvbgyiw+UNPNqL/KMLXf+1qevoWmn85cxEpKC3G2nZ5ySIQ3c2VWLrrikk
|
||||
Q7FoiYB10v+xfW4Ge4UZSTYaP+xn/vb2FtxG4s7HxGkI6gk5GTLLI2qQg1lPodMWn0bTAiu69pyNqTof
|
||||
/sgzjQmsZE6c0B0n3tvq2Ev9eQnSxtpMFYI4BAjPlMVr2lIj5ALaYsnE+mRAzDqrvEPLCgFDYMu/ih1U
|
||||
h6pH3XXh/dAJDU9Ba6X3ePjzZ5qm6aRNj3/7XyS14kz46ShYWFEXNFigQPuRkxo5B7nfTHGjTHopPu9M
|
||||
8OF9u56/KEoOvVdLt2mpq4LdpX+S+E9O9xtp/OB4mTbNKZ1Nr9g08XI2a+bLs7nmBmTzFTYMwtWq3rnj
|
||||
vxrgyMid2+QR5GMKzb1HfduD6/G4LuX0as7Yg9+wn4fRz2ozbEzKlfo4QBYGKNEiE68zbBGId7Ka71sp
|
||||
bABeUMkV2LkYBcH55JNmMU5G716RuaGTnAUz9f9STkxXlmBMNNogxsPDCzu/BIjLdhpQTr/d9iPfWGBK
|
||||
DSAJk5zcNayPhPh2ch8K93GCX0K/O9daCepQtUSwAkQyyPxbDLoei/8l5PdHFzAIJ8R8/v0w/g0AAP//
|
||||
ePEOly4LAAA=
|
||||
H4sIAAAAAAAA/6xX62rrOBD+36cQhIVTWBmnpzltHDgs9LTvIVuKPUSWjCQnaZe++6KLHcuWuxcW+iOW
|
||||
5vLNzDczamNajv68Q6gFgRsGdWMKtM3z3w53n3d3paTv7raSXKoCbZ6eng5BmDU1RMLESRp2NZiySipi
|
||||
QIoCCSmYu6dwziopDBPGSXaEUhB1gba77npIQhgdl5xUp9FKQ/jRmbgANU2BdgFB1jBCmfLxEFWDwJwd
|
||||
TYFyf90paImKI8rzp2f2FN3jsnYiJalOtZK9oDghDaLrTXYEzuJosh1rUfaDtRY+Bd1x8l4gEBwEwyWX
|
||||
Ng6ESqkoUwXadlekJQeKNlVV+Zsr1g2h8mK1NDMod1LfuyvaUEpv2lgRCr0u0KPN3whJEXECUWP3FafJ
|
||||
53mmvZueRoheXl4OQ0kJh1oUqGLCMOXJ0RsjRab7sgWDz9LYRFgUWSvPDEMlhc90r7RNntBYMQ0fng2b
|
||||
lol+im6o+MCAx4C2kxo8kxTjxMCZHaLaFGiz3W+ft8/2+MLKExgsz0wdubxgXSnJuauLkX3VTFxnXa8Y
|
||||
tj+xpQ0IX/SViqXUhLQip1SDxGzYzsgwsmClWRC6NGAY1h2pmD28KNJNMTj3Pv8p0HHaSKkl7407NbIr
|
||||
UEis8nkOX6FfnMA2PlNDS/rTEKqD+EUQtqE8TmmMbKNSz5jonY68C5Zn4drRQUCE9h4jHjKWjjeO0fkZ
|
||||
eJXo7oFHa55/9nxtMExU3YSSCj6sGg8UdHojLa8F8se+bds6M03floKAF5xMwdDaN4G1RIykGpUf1iN9
|
||||
fX0NI88TNR5hu8GpI+BknM6vsAbK9HLe7lL8mamemTJQET7VdgWKlT17Jtol1HiJazsmyiK4baIwtuyh
|
||||
vc4clsW1O3X3fr4tBCZjL4hgEoqaEkNoiG55MyAmvZHOoCElZ4NjQ7/y7UWHmgXZZdnciPLjATOlpFpj
|
||||
7a9feZ7nk6bevL29LW0YWdec4SMo7XN361tf3HGXxH3ta5cS+Uw6aIHSsE5nOyoeSJ5keBs584fz7ZV2
|
||||
xMkyEKv/dRwpif+CKIJUUNC2qnStRmEJD5/7/T4MKEkJd5uOE/+kOYOGEjiY9wI1QCkT64MxvIsmczH8
|
||||
XtnGw3ea7R8YBGVXJ5anR46qS/It/x2Fv2x3nwjjJ4Vz/GKJ0hwGYmidZLYcf5OPmQFZTKxhqS0eoysd
|
||||
+EfLKBD0zb5VA8jHnLX3DvXtTbNcdctUTktzhCtzL7vPu9HOYssntt5CfFwOMwUQYIDwQ4QtAHFGFrs6
|
||||
FUIC8IxKNsF+YIcDb3zyMJ/1zGjdCRK7ULIjJ7r5V8LZbcz9zaCLRl1gy8Ka7quKab1u7+FhT477L+1F
|
||||
/+usx3HjlK4UYwIRQdG3llwDvb7v7BPyPuz6sx8d1rSSHFtUHeKkZDwbztxXcLpcgf/E5Y9H69AfTmj+
|
||||
/P/D+CsAAP//kMgK/X4OAAA=
|
||||
`,
|
||||
},
|
||||
|
||||
@ -315,22 +318,22 @@ H4sIAAAAAAAA/0rOzyvOz0nVy8lP11DydA5RcE/MTfVKzFVwTMnNzFPStOYCBAAA//8imS6KIgAAAA==
|
||||
|
||||
"/assets/js/gjvote.js": {
|
||||
local: "assets/js/gjvote.js",
|
||||
size: 2430,
|
||||
modtime: 1497536571,
|
||||
size: 2455,
|
||||
modtime: 1498145831,
|
||||
compressed: `
|
||||
H4sIAAAAAAAA/6RWT2+rOBC/8ylG6cGgtGi1l5XComq3W2lXaquV2ttqDwRPwiiOnYcNeajNd3+yDYSk
|
||||
IUn1emiL5/cHz4zHLCqZG1ISjFouBf7B1yT/zSSKMIL3AICrvFqjNPG3CsvmFQXmRpUhu1mjrFgU5yLT
|
||||
+om0ib1AyAriHCWLkmAXBEHPV3KFDVdbCSl0piHWxttgbSB1vz8+YEuSq22MNUqTBAB1VgLpR51nG7Ts
|
||||
TGi067QIJytsJkASeiUYQq1BvMIG0hQmfnViHY6WJ5GV2wEKjeMiD4qjZfz6m4f7N+iAnfvnPHrwLgj6
|
||||
XOtCbZ8Vz0SoNnZBe7Ld59ouQ7rP+xLNo0D775/NPzxkDnCnaixF1jAnfgFryAhkUUxSYvmG322mO+PY
|
||||
BaP7g8fZZHKFrK7mZ5W7+F68W2n1adFj54o3XQIv2FrokeVQZVDII/0XxfFrHtlmg5I/FCT4Z6VBC/Sx
|
||||
ypi+mAALVYaudSGFXxIg+B2OkLFAuTRFAjSddizfBnMjk/bxs8F/9H9M+rWar8nsaWBJw87JS8wMttsL
|
||||
mXZ43zH256DdL9OzAbP9e+K1XH2HXXAcS9NKclyQRB7dMzYbwyXjHkWJixELGzp0uDlpYXFnHHJB+WrE
|
||||
wsUOPPppFr3vTpk5xlm3TOtRt0zrK3LmcGc8NkqTGz2nbbrwoVNJy8KctOvwSRD0veOP5N9vz0/DIzlS
|
||||
VovveuUabFvzC6W0SJeJl2xtlZneZDnCpirxzlOATceyN2Wng/u9ehMts3wVC9IGJZZhf3zepeI4mxt5
|
||||
C+7mmjFXeLa77SFjzdEC+gN2aTx5+tGEmhvZCuza0eTQsTaNwLgmTXMSZBqbF/ckkCUH11JBHP219HP3
|
||||
0bhv+23w1UurbSp2DXF4Pxzw3Fj1mfuL6stb6rMcAGwLEhju2fGCSm1c3rsBPAiWuFY1tlU5yek+CX4E
|
||||
AAD///kqVXt+CQAA
|
||||
H4sIAAAAAAAA/6RWT0/jOhC/51OMysGJChF6lye1L0Lv8ZB2JUArwW21hzSeNqO6djd20o2g331lO0nT
|
||||
0rRFywGI5/cnnhl7Mi9lZkhJMGqxEPgvX5H8lkoUYQRvAQBXWblCaeKfJRb1CwrMjCpCdrVCWbIozkSq
|
||||
9SNpE3uBkOXEOUoWTYNtEAQdX8kl1lxtJCTQmoZYGW+DlYHE/X5/hw1JrjYxVijNNACo0gJIP+gsXaNl
|
||||
p0KjXad5OFpiPQKS0ClBH2oN4iXWkCQw8qsj63CwPIqs3BZQaBwWuVccLeOvvz3cv0ELbN0/5tGDt0HQ
|
||||
5VrnavOkeCpCtbYL2pPtPld2GZJd3hdoHgTaf/+rv/KQOcCNqrAQac2c+BmsISOQRTFJicUr/rKZbo1j
|
||||
F4zu9h4no9EFsrqcnVRu4zvxdqXRp3mHnSletwk8Y2uhB5Z9lV4hD/SfFcfPeaTrNUp+n5PgH5V6LdDF
|
||||
SmO6YgLMVRG61oUEbqdA8A8cIGOBcmHyKdB43LJ8G8yMnDaPHw2+04+Y9Es5W5HZ0cCS+p2TFZgabLYX
|
||||
Mu3wvmPsz167n6enPWbz98hrufr2u+AwliSl5DgniTy6Y2wyhJsOe+QFzgcsbGjf4eqohcWdcMgEZcsB
|
||||
Cxfb8+hus+hte8zMMU66pVoPuqVaX5AzhzvhsVaa3NVz3KYN7zsVtMjNUbsWPw2Crnf8kfzy+vTYP5ID
|
||||
ZbX4tlcuwTY1P1NKi3SZeE5XVpnpdZohrMsCbzwF2Hgoe2N2PLjbqzfRMs2WsSBtUGIRdsfnTSqOk5mR
|
||||
1+Am14S5wrPtdQcZao4G0B2wc9eTpx/cUDMjG4FtczU5dKxNLTCuSNOMBJna5sU9CWQW30xbnRVKiFcV
|
||||
3l7DrR/e3bzKiaOfV382qIZfqPlo+Ow0a7qNXULsD449nrtvfUr/p+r8lrr02+TlJDDcseM5Fdq4grQ3
|
||||
cy9Y4EpV2JTrKKf9VvgdAAD//3s8PKeXCQAA
|
||||
`,
|
||||
},
|
||||
|
||||
|
@ -7,16 +7,32 @@ body {
|
||||
min-ehgiht: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div.content {
|
||||
padding: 15px;
|
||||
min-height: 100%;
|
||||
color: black;
|
||||
}
|
||||
|
||||
div.half {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.primary {
|
||||
color: #0078e7;
|
||||
}
|
||||
|
||||
.primary-bg {
|
||||
background-color: #0078e7;
|
||||
}
|
||||
|
||||
input.file {
|
||||
padding: .5em .6em;
|
||||
display: inline-block;
|
||||
@ -25,6 +41,20 @@ input.file {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
input.ranking-input {
|
||||
width: 50px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #CCC;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button.submit-vote {
|
||||
}
|
||||
|
||||
i.move-icon {
|
||||
cursor: ns-resize;
|
||||
}
|
||||
|
||||
#menu {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
@ -100,6 +130,11 @@ img.thumbnail {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.space-vertical {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.big-space {
|
||||
margin: 10px;
|
||||
}
|
||||
@ -139,16 +174,21 @@ table.padding td {
|
||||
}
|
||||
.pure-button-toggle-middle {
|
||||
border-radius: 0px;
|
||||
margin-left: -5px;
|
||||
margin-left: -1px;
|
||||
border-left: 1px solid #CCC;
|
||||
}
|
||||
.pure-button-toggle-last {
|
||||
border-top-left-radius: 0px;
|
||||
border-bottom-left-radius: 0px;
|
||||
margin-left: -5px;
|
||||
margin-left: -1px;
|
||||
border-left: 1px solid #CCC;
|
||||
}
|
||||
|
||||
.pure-button:disabled {
|
||||
background-color: #CCC;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
#modal-overlay {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
|
7
main.go
7
main.go
@ -51,7 +51,7 @@ type pageData struct {
|
||||
HideAdminMenu bool
|
||||
session *pageSession
|
||||
CurrentJam string
|
||||
ClientID string
|
||||
ClientId string
|
||||
ClientIsAuth bool
|
||||
ClientIsServer bool
|
||||
TeamID string
|
||||
@ -91,6 +91,7 @@ func main() {
|
||||
// Public Subrouter
|
||||
pub := r.PathPrefix("/").Subrouter()
|
||||
pub.HandleFunc("/", handleMain)
|
||||
pub.HandleFunc("/vote", handlePublicSaveVote)
|
||||
|
||||
// API Subrouter
|
||||
//api := r.PathPrefix("/api").Subtrouter()
|
||||
@ -265,8 +266,8 @@ func InitPageData(w http.ResponseWriter, req *http.Request) *pageData {
|
||||
p.FlashClass = "error"
|
||||
}
|
||||
|
||||
p.ClientID = p.session.getClientID()
|
||||
p.ClientIsAuth = clientIsAuthenticated(p.ClientID, req)
|
||||
p.ClientId = p.session.getClientId()
|
||||
p.ClientIsAuth = clientIsAuthenticated(p.ClientId, req)
|
||||
p.ClientIsServer = clientIsServer(req)
|
||||
// TeamID is for team self-administration
|
||||
p.TeamID, _ = p.session.getStringValue("teamid")
|
||||
|
@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/pborman/uuid"
|
||||
)
|
||||
@ -217,10 +216,8 @@ func dbGetTeamMembers(teamid string) ([]TeamMember, error) {
|
||||
ret = append(ret, *mbr)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
return ret, nil
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func dbGetTeamMember(teamid, mbrid string) (*TeamMember, error) {
|
||||
|
@ -1,10 +1,21 @@
|
||||
package main
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A Choice is a ranking of a game in a vote
|
||||
type GameChoice struct {
|
||||
Team string // UUID of team
|
||||
Rank int
|
||||
}
|
||||
|
||||
// A Vote is a collection of game rankings
|
||||
type Vote struct {
|
||||
Timestamp time.Time
|
||||
ClientId string
|
||||
ClientId string // UUID of client
|
||||
Choices []GameChoice
|
||||
}
|
||||
|
||||
func dbGetAllVotes() []Vote {
|
||||
@ -16,19 +27,70 @@ func dbGetAllVotes() []Vote {
|
||||
defer db.CloseDB()
|
||||
|
||||
votesBkt := []string{"votes"}
|
||||
var clients []string
|
||||
if clients, err = db.GetBucketList(votesBkt); err != nil {
|
||||
// Couldn't get the list of clients
|
||||
return ret
|
||||
}
|
||||
for _, clid := range clients {
|
||||
ret = append(ret, dbGetClientVotes(clid)...)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func dbGetVote(clientId string, timestamp time.Time) *Vote {
|
||||
func dbGetClientVotes(clientId string) []Vote {
|
||||
var ret []Vote
|
||||
var err error
|
||||
if err = db.OpenDB(); err != nil {
|
||||
return nil
|
||||
return ret
|
||||
}
|
||||
defer db.CloseDB()
|
||||
|
||||
var times []string
|
||||
votesBkt := []string{"votes", clientId}
|
||||
if times, err = db.GetBucketList(votesBkt); err != nil {
|
||||
return ret
|
||||
}
|
||||
for _, t := range times {
|
||||
var tm time.Time
|
||||
if tm, err = time.Parse(time.RFC3339, t); err == nil {
|
||||
var vt *Vote
|
||||
if vt, err = dbGetVote(clientId, tm); err == nil {
|
||||
ret = append(ret, *vt)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func dbGetVote(clientId string, timestamp time.Time) (*Vote, error) {
|
||||
var err error
|
||||
if err = db.OpenDB(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer db.CloseDB()
|
||||
|
||||
vt := new(Vote)
|
||||
|
||||
return vt
|
||||
vt.Timestamp = timestamp
|
||||
vt.ClientId = clientId
|
||||
votesBkt := []string{"votes", clientId, timestamp.Format(time.RFC3339)}
|
||||
var choices []string
|
||||
if choices, err = db.GetBucketList(votesBkt); err != nil {
|
||||
// Couldn't find the vote...
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range choices {
|
||||
ch := new(GameChoice)
|
||||
var rank int
|
||||
if rank, err = strconv.Atoi(v); err == nil {
|
||||
ch.Rank = rank
|
||||
ch.Team, err = db.GetValue(votesBkt, v)
|
||||
}
|
||||
if err == nil {
|
||||
vt.Choices = append(vt.Choices, *ch)
|
||||
}
|
||||
}
|
||||
return vt, nil
|
||||
}
|
||||
|
||||
func dbSaveVote(clientId string, timestamp time.Time, votes []string) error {
|
||||
@ -37,8 +99,10 @@ func dbSaveVote(clientId string, timestamp time.Time, votes []string) error {
|
||||
return nil
|
||||
}
|
||||
defer db.CloseDB()
|
||||
|
||||
votesBkt := []string{"votes", clientId}
|
||||
|
||||
// Make sure we don't clobber a duplicate vote
|
||||
votesBkt := []string{"votes", clientId, timestamp.Format(time.RFC3339)}
|
||||
for i := range votes {
|
||||
db.SetValue(votesBkt, strconv.Itoa(i), votes[i])
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ func (p *pageSession) setStringValue(key, val string) {
|
||||
p.session.Save(p.req, p.w)
|
||||
}
|
||||
|
||||
func (p *pageSession) getClientID() string {
|
||||
func (p *pageSession) getClientId() string {
|
||||
var clientId string
|
||||
var err error
|
||||
if clientId, err = p.getStringValue("client_id"); err != nil {
|
||||
|
@ -2,6 +2,8 @@ package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func initPublicPage(w http.ResponseWriter, req *http.Request) *pageData {
|
||||
@ -16,6 +18,44 @@ func handleMain(w http.ResponseWriter, req *http.Request) {
|
||||
case SiteModeWaiting:
|
||||
page.show("public-waiting.html", w)
|
||||
case SiteModeVoting:
|
||||
loadVotingPage(w, req)
|
||||
}
|
||||
}
|
||||
|
||||
func loadVotingPage(w http.ResponseWriter, req *http.Request) {
|
||||
page := initPublicPage(w, req)
|
||||
type votingPageData struct {
|
||||
Teams []Team
|
||||
Timestamp string
|
||||
}
|
||||
vpd := new(votingPageData)
|
||||
vpd.Teams = dbGetAllTeams()
|
||||
vpd.Timestamp = time.Now().Format(time.RFC3339)
|
||||
page.TemplateData = vpd
|
||||
page.show("public-voting.html", w)
|
||||
}
|
||||
|
||||
func handlePublicSaveVote(w http.ResponseWriter, req *http.Request) {
|
||||
page := initPublicPage(w, req)
|
||||
page.SubTitle = ""
|
||||
|
||||
// Check if we already have a vote for this client id/timestamp
|
||||
ts := req.FormValue("timestamp")
|
||||
timestamp, err := time.Parse(time.RFC3339, ts)
|
||||
if err != nil {
|
||||
page.session.setFlashMessage("Error parsing timestamp: "+ts, "error")
|
||||
redirect("/", w, req)
|
||||
}
|
||||
if _, err := dbGetVote(page.ClientId, timestamp); err == nil {
|
||||
// Duplicate vote... Cancel it.
|
||||
page.session.setFlashMessage("Duplicate vote!", "error")
|
||||
redirect("/", w, req)
|
||||
}
|
||||
// voteSlice is an ordered string slice of the voters preferences
|
||||
voteCSV := req.FormValue("uservote")
|
||||
voteSlice := strings.Split(voteCSV, ",")
|
||||
if err := dbSaveVote(page.ClientId, timestamp, voteSlice); err != nil {
|
||||
page.session.setFlashMessage("Error Saving Vote: "+err.Error(), "error")
|
||||
}
|
||||
redirect("/", w, req)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
{{ if not .TemplateData.Games }}
|
||||
{{ if not .TemplateData.Teams }}
|
||||
<div>No games have been created</div>
|
||||
{{ else }}
|
||||
<table id="games-table" class="sortable pure-table pure-table-bordered center">
|
||||
@ -10,11 +10,11 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ range $i, $v := .TemplateData.Games }}
|
||||
{{ range $i, $v := .TemplateData.Teams }}
|
||||
<tr>
|
||||
<td>{{ $v.Game.Name }}</td>
|
||||
<td>{{ $v.Name }}</td>
|
||||
<td></td>
|
||||
<td>{{ len $v.Screenshots }}</td>
|
||||
<td>{{ len $v.Game.Screenshots }}</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
|
@ -13,10 +13,10 @@
|
||||
</ul>
|
||||
{{ if .ClientIsAuth }}
|
||||
{{ if not .ClientIsServer }}
|
||||
<a href="/admin/clients/{{.ClientID}}/remove" class="pure-menu-link"><i class="fa fa-key"></i> DeAuth Client</a>
|
||||
<a href="/admin/clients/{{.ClientId}}/remove" class="pure-menu-link"><i class="fa fa-key"></i> DeAuth Client</a>
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<a href="/admin/clients/{{.ClientID}}/add" class="pure-menu-link"><i class="fa fa-key"></i> Auth Client</a>
|
||||
<a href="/admin/clients/{{.ClientId}}/add" class="pure-menu-link"><i class="fa fa-key"></i> Auth Client</a>
|
||||
{{ end }}
|
||||
<ul class="pure-menu-list menu-bottom">
|
||||
{{ range $k, $v := .BottomMenu }}
|
||||
|
@ -1,13 +1,10 @@
|
||||
<script>
|
||||
var clientID = "{{.ClientID}}";
|
||||
var clientId = "{{.ClientId}}";
|
||||
</script>
|
||||
<aside class="flash center {{.FlashClass}}">
|
||||
{{.FlashMessage}}
|
||||
</aside>
|
||||
<div class="content">
|
||||
<div class="header">
|
||||
devICT Game Jam - {{.CurrentJam}}
|
||||
</div>
|
||||
{{ if .SubTitle }}
|
||||
<div class="header-menu">
|
||||
<h2>{{.SubTitle}}</h2>
|
||||
|
@ -1 +1,304 @@
|
||||
<h1>VOTING TIME</h1>
|
||||
{{ if not .TemplateData.Teams }}
|
||||
<div>No games have been created</div>
|
||||
{{ else }}
|
||||
<div class="content">
|
||||
Rank one or more games from your favorite to least favorite.
|
||||
</div>
|
||||
<div class="content">
|
||||
<h2>Your Choices</h2>
|
||||
<table id="ranked-table" class="pure-table pure-table-bordered center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rank</th>
|
||||
<th>Game Name</th>
|
||||
<th>Team Name</th>
|
||||
<th>Screenshots</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h2>Unranked Games</h2>
|
||||
<table id="unranked-table" class="pure-table pure-table-bordered center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Game Name</th>
|
||||
<th>Team Name</th>
|
||||
<th>Screenshots</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ range $i, $v := .TemplateData.Teams }}
|
||||
<tr id="teamrow-{{$v.UUID}}" data-teamid="{{$v.UUID}}">
|
||||
<td class="unranked-actions"><a class="pure-button pure-button-primary" href="javascript:moveToRanked('{{$v.UUID}}');"><i class="fa fa-plus"></i> Add to Vote</a></td>
|
||||
<td class="voting-col game-name">{{ $v.Game.Name }}</td>
|
||||
<td class="voting-col team-name">{{ $v.Name }}</td>
|
||||
<td class="voting-col game-screenshots" data-sscount="{{len $v.Game.Screenshots}}">
|
||||
{{ if not $v.Game.Screenshots }}
|
||||
<i class="fa fa-image"></i> (No Screenshots)
|
||||
{{ else }}
|
||||
<a class="primary" tabindex="-1" href="javascript:showScreenshots({{$v.UUID}});"><i class="fa fa-image"></i> ({{ len $v.Game.Screenshots }})</a>
|
||||
{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="content half">
|
||||
<form action="/vote">
|
||||
<input id="uservote" type="hidden" name="uservote" value="" />
|
||||
<input id="timestamp" type="hidden" name="timestamp" value="{{.TemplateData.Timestamp}}" />
|
||||
<button class="pure-button pure-button-primary space-vertical pull-right" type="submit">Submit Vote!</button>
|
||||
</form>
|
||||
</div>
|
||||
{{ end }}
|
||||
<script>
|
||||
|
||||
function updateView() {
|
||||
updateButtonStates();
|
||||
var rankedCells = snack.wrap('#ranked-table>tbody>tr>td.rank-cell');
|
||||
for(var i = 0; i < rankedCells.length; i++) {
|
||||
rankedCells[i].innerText = i+1;
|
||||
}
|
||||
setUserVoteValue();
|
||||
}
|
||||
|
||||
function setUserVoteValue() {
|
||||
document.getElementById('uservote').value=getRankedCSV();
|
||||
}
|
||||
|
||||
// moveToRanked takes the uuid of a game/team and moves that game to the
|
||||
// bottom of the 'ranked' table
|
||||
function moveToRanked(tmUUID) {
|
||||
// First, find the team's row
|
||||
var rows = snack.wrap('#teamrow-'+tmUUID);
|
||||
if(rows.length > 0) {
|
||||
var row = rows[0];
|
||||
var delCell = row.getElementsByClassName('unranked-actions');
|
||||
if(delCell.length > 0) {
|
||||
delCell = delCell[0];
|
||||
}
|
||||
delCell.remove();
|
||||
var tbody = snack.wrap('#ranked-table>tbody')[0];
|
||||
var rankTd = document.createElement('td');
|
||||
rankTd.classList.add('rank-cell');
|
||||
row.prepend(rankTd);
|
||||
row.append(createRankedActionsTd(tmUUID));
|
||||
tbody.append(row)
|
||||
}
|
||||
updateView();
|
||||
}
|
||||
|
||||
function moveRankedUp(tmUUID) {
|
||||
var rows = snack.wrap('#ranked-table>tbody>tr');
|
||||
var numRows = rows.length;
|
||||
var tbody = snack.wrap('#ranked-table>tbody')[0];
|
||||
if(rows.length > 0) {
|
||||
// Just loop through the rows adding them to the table
|
||||
// if the _next_ row is the row for this team, add it now
|
||||
for(var i = 0; i < numRows; i++) {
|
||||
if(numRows > i && rows[i+1] != null
|
||||
&& rows[i+1].dataset.teamid == tmUUID) {
|
||||
// The next one is the one we're moving up
|
||||
// Append the _next_ one, then this one
|
||||
tbody.append(rows[i+1]);
|
||||
tbody.append(rows[i]);
|
||||
// Increment i manually, since we already added the next row
|
||||
i++;
|
||||
} else {
|
||||
// Otherwise just add the row
|
||||
tbody.append(rows[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
updateView();
|
||||
}
|
||||
|
||||
function updateButtonStates() {
|
||||
var upBtns = snack.wrap('.ranked-move-up');
|
||||
for(var i = 0; i < upBtns.length; i++) {
|
||||
if(i == 0) {
|
||||
upBtns[i].disabled=true;
|
||||
} else {
|
||||
upBtns[i].disabled=false;
|
||||
}
|
||||
}
|
||||
var downBtns = snack.wrap('.ranked-move-down');
|
||||
for(var i = 0; i < downBtns.length; i++) {
|
||||
if(i == downBtns.length-1) {
|
||||
downBtns[i].disabled=true;
|
||||
} else {
|
||||
downBtns[i].disabled=false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function moveRankedDown(tmUUID) {
|
||||
var rows = snack.wrap('#ranked-table>tbody>tr');
|
||||
var numRows = rows.length;
|
||||
var tbody = snack.wrap('#ranked-table>tbody')[0];
|
||||
if(numRows > 0) {
|
||||
// Just loop through the rows adding them to the table
|
||||
// When we hit the row for this team, delay it by one
|
||||
for(var i = 0; i < numRows; i++) {
|
||||
if(rows[i].dataset.teamid == tmUUID && numRows > i) {
|
||||
// This is the one we're moving down
|
||||
// Append the _next_ one, then this one
|
||||
tbody.append(rows[i+1]);
|
||||
tbody.append(rows[i]);
|
||||
// Increment i manually, since we already added the next row
|
||||
i++;
|
||||
} else {
|
||||
// Otherwise just add the row
|
||||
tbody.append(rows[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
updateView();
|
||||
}
|
||||
|
||||
// moveToRanked takes the uuid of a game/team and moves that game to the
|
||||
// bottom of the 'unranked' table
|
||||
function moveToUnranked(tmUUID) {
|
||||
// First, find the team's row
|
||||
var rows = snack.wrap('#teamrow-'+tmUUID);
|
||||
if(rows.length > 0) {
|
||||
var row = rows[0];
|
||||
// Remove the cells we don't need
|
||||
var actCell = row.getElementsByClassName('ranked-actions');
|
||||
if(actCell.length > 0) {
|
||||
actCell = actCell[0];
|
||||
}
|
||||
actCell.remove();
|
||||
var rankTd = row.getElementsByClassName('rank-cell');
|
||||
if(rankTd.length > 0) {
|
||||
rankTd = rankTd[0];
|
||||
}
|
||||
rankTd.remove();
|
||||
// Add the cells we do
|
||||
row.prepend(createUnrankedActionsTd(tmUUID));
|
||||
// And add the row to the unranked table
|
||||
var tbody = snack.wrap('#unranked-table>tbody')[0];
|
||||
tbody.append(row);
|
||||
}
|
||||
updateView();
|
||||
}
|
||||
|
||||
// getUnranked returns an array of games that haven't been ranked yet
|
||||
// (it builds the array from the 'unranked' table)
|
||||
function getUnranked() {
|
||||
return gameTableToArray('unranked');
|
||||
}
|
||||
|
||||
// getRanked returns an array of games that the user has ranked
|
||||
// (it builds the array from the 'ranked' table)
|
||||
function getRanked() {
|
||||
return gameTableToArray('ranked');
|
||||
}
|
||||
|
||||
// Converts either the 'ranked' or 'unranked' table to an array of objects
|
||||
// 'tbl' should be either 'ranked' or 'unranked'
|
||||
function gameTableToArray(tbl) {
|
||||
var ret = [];
|
||||
snack.wrap('#'+tbl+'-table>tbody>tr').each(function(ele, idx) {
|
||||
ret = ret.concat(getTeamObj(ele.dataset.teamid));
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
// getTeamObj returns an object for team tmUUID from table
|
||||
function getTeamObj(tmUUID) {
|
||||
var ret = null;
|
||||
var rows = snack.wrap('#teamrow-'+tmUUID);
|
||||
if(rows.length > 0) {
|
||||
var ele = rows[0];
|
||||
ret = {
|
||||
uuid: tmUUID,
|
||||
name: ele.getElementsByClassName('game-name')[0].innerText,
|
||||
teamName: ele.getElementsByClassName('team-name')[0].innerText,
|
||||
ssCount: ele.getElementsByClassName('game-screenshots')[0].dataset.sscount
|
||||
};
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// getRankedCSV pulls the getRanked array and just returns a CSV of
|
||||
// the team IDs in ranked order
|
||||
// (This is how the 'vote' post expects it)
|
||||
function getRankedCSV() {
|
||||
var r = getRanked();
|
||||
var ret = "";
|
||||
for(var i = 0; i < r.length; i++) {
|
||||
ret = ret + r[i].uuid+",";
|
||||
}
|
||||
// Remove the trailing ","
|
||||
if(ret.endsWith(",")) {
|
||||
ret = ret.slice(0, ret.length-2);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// createRankedActionsTd creates the td that holds all of the action
|
||||
// buttons for a 'ranked' table row
|
||||
function createRankedActionsTd(tmUUID) {
|
||||
var td = document.createElement('td');
|
||||
td.classList.add('ranked-actions');
|
||||
var upBtn = document.createElement('button');
|
||||
upBtn.classList.add('ranked-move-up', 'pure-button', 'pure-button-toggle-first', 'pure-button-primary');
|
||||
upBtn.innerHTML = '<i class="fa fa-arrow-up"></i> Move Up';
|
||||
snack.listener({
|
||||
node: upBtn,
|
||||
event: 'click'
|
||||
}, function() {
|
||||
moveRankedUp(tmUUID);
|
||||
});
|
||||
td.appendChild(upBtn);
|
||||
var dnBtn = document.createElement('button');
|
||||
dnBtn.classList.add('ranked-move-down', 'pure-button', 'pure-button-toggle-middle', 'pure-button-primary');
|
||||
dnBtn.innerHTML = '<i class="fa fa-arrow-down"></i> Move Down';
|
||||
snack.listener({
|
||||
node: dnBtn,
|
||||
event: 'click'
|
||||
}, function() {
|
||||
moveRankedDown(tmUUID);
|
||||
});
|
||||
td.appendChild(dnBtn);
|
||||
var delBtn = document.createElement('button');
|
||||
delBtn.dataset.teamid=tmUUID
|
||||
delBtn.classList.add('ranked-remove', 'pure-button', 'pure-button-toggle-last', 'pure-button-error');
|
||||
delBtn.innerHTML = '<i class="fa fa-times"></i> Remove';
|
||||
snack.listener({
|
||||
node: delBtn,
|
||||
event: 'click'
|
||||
}, function (){
|
||||
moveToUnranked(tmUUID);
|
||||
});
|
||||
td.appendChild(delBtn);
|
||||
return td;
|
||||
}
|
||||
|
||||
// createUnrankedActionsTd created the td that holds the 'Add to Vote' button
|
||||
function createUnrankedActionsTd(tmUUID) {
|
||||
var td = document.createElement('td');
|
||||
td.classList.add('unranked-actions');
|
||||
var addBtn = document.createElement('button');
|
||||
addBtn.dataset.teamid=tmUUID
|
||||
addBtn.classList.add('pure-button', 'pure-button-toggle-last', 'pure-button-primary');
|
||||
addBtn.innerHTML = '<i class="fa fa-plus"></i> Add to Vote';
|
||||
var params = {
|
||||
node: addBtn,
|
||||
event: 'click'
|
||||
}
|
||||
snack.listener(params, function (){
|
||||
moveToRanked(addBtn.dataset.teamid);
|
||||
})
|
||||
td.appendChild(addBtn);
|
||||
return td;
|
||||
}
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user