diff --git a/internal/db/bookings.sql.go b/internal/db/bookings.sql.go index bceef5a..92b56ab 100644 --- a/internal/db/bookings.sql.go +++ b/internal/db/bookings.sql.go @@ -105,6 +105,83 @@ func (q *Queries) GetBooking(ctx context.Context, id int64) (GetBookingRow, erro return i, err } +const listAllBookingsWithDetails = `-- name: ListAllBookingsWithDetails :many +SELECT + b.id, + b.user_id, + b.service_id, + b.event_date, + b.address, + b.notes, + b.created_at, + b.status, + b.service_option, + b.event_type, + u.email as user_email, + s.name as service_name, + s.description as service_description, + s.price_cents as service_price_cents +FROM bookings b +JOIN users u ON b.user_id = u.id +JOIN services s ON b.service_id = s.id +ORDER BY b.created_at DESC +` + +type ListAllBookingsWithDetailsRow struct { + ID int64 `json:"id"` + UserID sql.NullInt64 `json:"user_id"` + ServiceID sql.NullInt64 `json:"service_id"` + EventDate time.Time `json:"event_date"` + Address sql.NullString `json:"address"` + Notes sql.NullString `json:"notes"` + CreatedAt sql.NullTime `json:"created_at"` + Status sql.NullString `json:"status"` + ServiceOption sql.NullString `json:"service_option"` + EventType sql.NullString `json:"event_type"` + UserEmail string `json:"user_email"` + ServiceName string `json:"service_name"` + ServiceDescription sql.NullString `json:"service_description"` + ServicePriceCents int32 `json:"service_price_cents"` +} + +func (q *Queries) ListAllBookingsWithDetails(ctx context.Context) ([]ListAllBookingsWithDetailsRow, error) { + rows, err := q.db.QueryContext(ctx, listAllBookingsWithDetails) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ListAllBookingsWithDetailsRow{} + for rows.Next() { + var i ListAllBookingsWithDetailsRow + if err := rows.Scan( + &i.ID, + &i.UserID, + &i.ServiceID, + &i.EventDate, + &i.Address, + &i.Notes, + &i.CreatedAt, + &i.Status, + &i.ServiceOption, + &i.EventType, + &i.UserEmail, + &i.ServiceName, + &i.ServiceDescription, + &i.ServicePriceCents, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const listBookingsByUser = `-- name: ListBookingsByUser :many SELECT id, user_id, service_id, event_date, address, notes, created_at, status, service_option, event_type FROM bookings @@ -232,3 +309,19 @@ func (q *Queries) ListBookingsWithServiceByUser(ctx context.Context, userID sql. } return items, nil } + +const updateBookingStatus = `-- name: UpdateBookingStatus :exec +UPDATE bookings +SET status = $2 +WHERE id = $1 +` + +type UpdateBookingStatusParams struct { + ID int64 `json:"id"` + Status sql.NullString `json:"status"` +} + +func (q *Queries) UpdateBookingStatus(ctx context.Context, arg UpdateBookingStatusParams) error { + _, err := q.db.ExecContext(ctx, updateBookingStatus, arg.ID, arg.Status) + return err +} diff --git a/internal/db/models.go b/internal/db/models.go index fc3c3a3..f962fad 100644 --- a/internal/db/models.go +++ b/internal/db/models.go @@ -43,4 +43,5 @@ type User struct { Email string `json:"email"` CreatedAt sql.NullTime `json:"created_at"` OryIdentityID sql.NullString `json:"ory_identity_id"` + IsAdmin sql.NullBool `json:"is_admin"` } diff --git a/internal/db/queries/bookings.sql b/internal/db/queries/bookings.sql index 9958077..cf62079 100644 --- a/internal/db/queries/bookings.sql +++ b/internal/db/queries/bookings.sql @@ -33,3 +33,29 @@ ORDER BY b.event_date DESC; SELECT id, user_id, service_id, event_date, address, notes, created_at, status, service_option, event_type FROM bookings WHERE id = $1; + +-- name: ListAllBookingsWithDetails :many +SELECT + b.id, + b.user_id, + b.service_id, + b.event_date, + b.address, + b.notes, + b.created_at, + b.status, + b.service_option, + b.event_type, + u.email as user_email, + s.name as service_name, + s.description as service_description, + s.price_cents as service_price_cents +FROM bookings b +JOIN users u ON b.user_id = u.id +JOIN services s ON b.service_id = s.id +ORDER BY b.created_at DESC; + +-- name: UpdateBookingStatus :exec +UPDATE bookings +SET status = $2 +WHERE id = $1; diff --git a/internal/db/users.sql.go b/internal/db/users.sql.go index b289e3c..e36d3bc 100644 --- a/internal/db/users.sql.go +++ b/internal/db/users.sql.go @@ -13,7 +13,7 @@ import ( const createUser = `-- name: CreateUser :one INSERT INTO users (email, ory_identity_id) VALUES ($1, $2) -RETURNING id, email, created_at, ory_identity_id +RETURNING id, email, created_at, ory_identity_id, is_admin ` type CreateUserParams struct { @@ -29,12 +29,13 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, e &i.Email, &i.CreatedAt, &i.OryIdentityID, + &i.IsAdmin, ) return i, err } const getUserByEmail = `-- name: GetUserByEmail :one -SELECT id, email, created_at, ory_identity_id FROM users WHERE email = $1 LIMIT 1 +SELECT id, email, created_at, ory_identity_id, is_admin FROM users WHERE email = $1 LIMIT 1 ` func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error) { @@ -45,12 +46,13 @@ func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error &i.Email, &i.CreatedAt, &i.OryIdentityID, + &i.IsAdmin, ) return i, err } const getUserByOryID = `-- name: GetUserByOryID :one -SELECT id, email, created_at, ory_identity_id FROM users WHERE ory_identity_id = $1 LIMIT 1 +SELECT id, email, created_at, ory_identity_id, is_admin FROM users WHERE ory_identity_id = $1 LIMIT 1 ` func (q *Queries) GetUserByOryID(ctx context.Context, oryIdentityID sql.NullString) (User, error) { @@ -61,6 +63,7 @@ func (q *Queries) GetUserByOryID(ctx context.Context, oryIdentityID sql.NullStri &i.Email, &i.CreatedAt, &i.OryIdentityID, + &i.IsAdmin, ) return i, err } @@ -69,7 +72,7 @@ const updateUserOryID = `-- name: UpdateUserOryID :one UPDATE users SET ory_identity_id = $1 WHERE email = $2 -RETURNING id, email, created_at, ory_identity_id +RETURNING id, email, created_at, ory_identity_id, is_admin ` type UpdateUserOryIDParams struct { @@ -85,6 +88,7 @@ func (q *Queries) UpdateUserOryID(ctx context.Context, arg UpdateUserOryIDParams &i.Email, &i.CreatedAt, &i.OryIdentityID, + &i.IsAdmin, ) return i, err } diff --git a/internal/handlers/catalog.go b/internal/handlers/catalog.go index b697d24..da492a6 100644 --- a/internal/handlers/catalog.go +++ b/internal/handlers/catalog.go @@ -4,10 +4,12 @@ import ( "html/template" "net/http" + ory "github.com/ory/client-go" + "decor-by-hannahs/internal/db" ) -func CatalogHandler(q *db.Queries, tmpl *template.Template) http.HandlerFunc { +func CatalogHandler(q *db.Queries, tmpl *template.Template, oryClient *ory.APIClient) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { svcs, err := q.ListServices(r.Context()) if err != nil { @@ -15,10 +17,17 @@ func CatalogHandler(q *db.Queries, tmpl *template.Template) http.HandlerFunc { return } type data struct { - Services []db.Service - ActivePage string + Services []db.Service + ActivePage string + Authenticated bool + OryLoginURL string } - if err := tmpl.ExecuteTemplate(w, "catalog.tmpl", data{Services: svcs, ActivePage: "catalog"}); err != nil { + if err := tmpl.ExecuteTemplate(w, "catalog.tmpl", data{ + Services: svcs, + ActivePage: "catalog", + Authenticated: isAuthenticated(r, oryClient), + OryLoginURL: "/login", + }); err != nil { http.Error(w, "Failed to render page", http.StatusInternalServerError) } } diff --git a/internal/handlers/gallery.go b/internal/handlers/gallery.go index b45dfda..8044703 100644 --- a/internal/handlers/gallery.go +++ b/internal/handlers/gallery.go @@ -7,6 +7,8 @@ import ( "path/filepath" "sort" + ory "github.com/ory/client-go" + "decor-by-hannahs/internal/db" ) @@ -15,7 +17,7 @@ type Photo struct { Caption string } -func GalleryHandler(q *db.Queries, tmpl *template.Template) http.HandlerFunc { +func GalleryHandler(q *db.Queries, tmpl *template.Template, oryClient *ory.APIClient) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var photos []Photo @@ -40,10 +42,17 @@ func GalleryHandler(q *db.Queries, tmpl *template.Template) http.HandlerFunc { }) type data struct { - Photos []Photo - ActivePage string + Photos []Photo + ActivePage string + Authenticated bool + OryLoginURL string } - if err := tmpl.ExecuteTemplate(w, "gallery.tmpl", data{Photos: photos, ActivePage: "gallery"}); err != nil { + if err := tmpl.ExecuteTemplate(w, "gallery.tmpl", data{ + Photos: photos, + ActivePage: "gallery", + Authenticated: isAuthenticated(r, oryClient), + OryLoginURL: "/login", + }); err != nil { http.Error(w, "Failed to render page", http.StatusInternalServerError) } } diff --git a/internal/tmpl/_partials.tmpl b/internal/tmpl/_partials.tmpl index 0a70383..bfaf051 100644 --- a/internal/tmpl/_partials.tmpl +++ b/internal/tmpl/_partials.tmpl @@ -54,8 +54,8 @@ - Decor By Hannah's - Decor By Hannah's + Decor By Hannahs + Decor By Hannahs @@ -83,7 +83,7 @@ {{end}} diff --git a/internal/tmpl/home.tmpl b/internal/tmpl/home.tmpl index 0a110c2..6e75f26 100644 --- a/internal/tmpl/home.tmpl +++ b/internal/tmpl/home.tmpl @@ -24,7 +24,7 @@
Party penguin Jester hat -

Welcome to Decor By Hannah's

+

Welcome to Decor By Hannahs

We take care of all the headache for your party decorations so you don't have to

Browse Services diff --git a/internal/tmpl/layout.tmpl b/internal/tmpl/layout.tmpl index ca4af9e..ab8bf3d 100644 --- a/internal/tmpl/layout.tmpl +++ b/internal/tmpl/layout.tmpl @@ -61,8 +61,8 @@
- Decor By Hannah's - Decor By Hannah's + Decor By Hannahs + Decor By Hannahs
@@ -85,7 +85,7 @@