Show respondent names on admin page

This commit is contained in:
Brandon Dyck 2020-10-04 14:54:41 -06:00
parent 235b826dbb
commit 3ad9b2955f
4 changed files with 56 additions and 35 deletions

View File

@ -215,8 +215,8 @@ type GetEventResponseSummaryQuery struct {
} }
type GetEventResponseSummaryResult struct { type GetEventResponseSummaryResult struct {
TotalResponses int RespondentNames []string
PerHourCounts map[date.Date]map[int]int PerHourCounts map[date.Date]map[int]int
} }
func (s *Store) GetEventResponseSummary(ctx context.Context, query GetEventResponseSummaryQuery) (GetEventResponseSummaryResult, error) { func (s *Store) GetEventResponseSummary(ctx context.Context, query GetEventResponseSummaryQuery) (GetEventResponseSummaryResult, error) {
@ -242,13 +242,14 @@ func (s *Store) GetEventResponseSummary(ctx context.Context, query GetEventRespo
} }
var result GetEventResponseSummaryResult var result GetEventResponseSummaryResult
const responseCountQuery = ` const respondentNameQuery = `
SELECT COUNT(*) SELECT guest_name
FROM response FROM response
WHERE event_id = ?;` WHERE event_id = ?
err = sqlitex.Exec(conn, responseCountQuery, ORDER BY guest_name ASC;`
err = sqlitex.Exec(conn, respondentNameQuery,
func(stmt *sqlite.Stmt) error { func(stmt *sqlite.Stmt) error {
result.TotalResponses = stmt.ColumnInt(0) result.RespondentNames = append(result.RespondentNames, stmt.ColumnText(0))
return nil return nil
}, query.EventID) }, query.EventID)
if err != nil { if err != nil {

View File

@ -2,7 +2,10 @@ package back_test
import ( import (
"context" "context"
"math/rand"
"sort"
"testing" "testing"
"time"
"github.com/matryer/is" "github.com/matryer/is"
"github.com/rickb777/date" "github.com/rickb777/date"
@ -61,29 +64,6 @@ func TestGetEventResponseSummary(t *testing.T) {
return event.EventID return event.EventID
} }
getTotalResponses := func(is *is.I, eventID string) int {
responses, err := store.GetEventResponseSummary(context.Background(), back.GetEventResponseSummaryQuery{
EventID: eventID,
})
is.NoErr(err)
return responses.TotalResponses
}
t.Run("TotalResponses counts the number of responses created", func(t *testing.T) {
is := is.New(t)
eventID := createEvent(is)
is.Equal(getTotalResponses(is, eventID), 0)
const respondTimes = 5
for i := 1; i <= respondTimes; i++ {
_, err = store.CreateEventResponse(context.Background(), back.CreateEventResponseCommand{
EventID: eventID,
})
is.NoErr(err)
is.Equal(getTotalResponses(is, eventID), i)
}
})
t.Run("does not include responses to other events", func(t *testing.T) { t.Run("does not include responses to other events", func(t *testing.T) {
is := is.New(t) is := is.New(t)
@ -109,11 +89,45 @@ func TestGetEventResponseSummary(t *testing.T) {
is.NoErr(err) is.NoErr(err)
is.Equal(summary, back.GetEventResponseSummaryResult{ is.Equal(summary, back.GetEventResponseSummaryResult{
TotalResponses: 0, RespondentNames: nil,
PerHourCounts: map[date.Date]map[int]int{}, PerHourCounts: map[date.Date]map[int]int{},
}) })
}) })
t.Run("RespondentNames lists all respondents alphabetically", func(t *testing.T) {
is := is.New(t)
var names = []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}
rand.Shuffle(len(names), func(i, j int) {
names[i], names[j] = names[j], names[i]
})
d := date.New(1963, time.May, 1)
event, err := store.CreateEvent(context.Background(), back.CreateEventCommand{
Name: "It's my party!",
Description: "I can cry if I want to.",
Latest: d,
Earliest: d,
})
is.NoErr(err)
for _, name := range names {
_, err = store.CreateEventResponse(context.Background(), back.CreateEventResponseCommand{
EventID: event.EventID,
GuestName: name,
})
is.NoErr(err)
}
summary, err := store.GetEventResponseSummary(context.Background(), back.GetEventResponseSummaryQuery{
EventID: event.EventID,
})
is.NoErr(err)
sort.Strings(names)
is.Equal(summary.RespondentNames, names)
})
t.Run("PerHourCounts counts votes per hour", func(t *testing.T) { t.Run("PerHourCounts counts votes per hour", func(t *testing.T) {
is := is.New(t) is := is.New(t)

View File

@ -365,6 +365,12 @@ func (h *handler) handleAdmin(w http.ResponseWriter, r *http.Request) {
return return
} }
// Build the respondents list
respondentsList := e.Ul()()
for _, r := range summary.RespondentNames {
respondentsList.Children = append(respondentsList.Children, e.Li()(hm.Text(r)))
}
// Build the counts table // Build the counts table
dateSpan := metadata.Latest.Sub(metadata.Earliest) dateSpan := metadata.Latest.Sub(metadata.Earliest)
var dates []date.Date var dates []date.Date
@ -426,8 +432,9 @@ func (h *handler) handleAdmin(w http.ResponseWriter, r *http.Request) {
a.Disabled(true), a.Disabled(true),
), ),
), ),
e.H3()(hm.Text("Responses")), e.H3()(hm.Text("Respondents")),
e.P()(hm.Text(strconv.Itoa(summary.TotalResponses))), respondentsList,
e.H3()(hm.Text("Results")),
countsTable, countsTable,
} }
_ = h.writePage(w, "Edit your event", body) _ = h.writePage(w, "Edit your event", body)

View File

@ -1,7 +1,6 @@
Essential: Essential:
------------ ------------
Show response after submission Show response after submission
Show respondent names on admin page
Make earliest and latest dates required for creation Make earliest and latest dates required for creation
Prevent blank event names and guest names Prevent blank event names and guest names
Consider some front-end, regex-based field validation Consider some front-end, regex-based field validation