feat(bookings): improve admin booking wizard and user dashboard
Backend: - Enriched GetAllUserBookings response with calculated total_amount, amount_paid, and duration_minutes. - Refactored GetBookingHandler to return a flat booking object matching frontend expectations. - Added account_role to admin user list response and sorted users by booking activity. - Corrected function name oo to AdminCreateBookingForUserHandler. Frontend: - Rebuilt BookingCreateModal into a 4-step wizard supporting guest bookings, service overrides, and real-time availability checks. - Fixed account dashboard logic to correctly identify upcoming vs past bookings and sort unpaid items to the top. - Extracted booking flow into a shared BookingFlow component. - Redirected admin users from home page to /today.
This commit is contained in:
@@ -78,10 +78,11 @@ type SocialLogin struct {
|
||||
}
|
||||
|
||||
type UserListItem struct {
|
||||
ID string `json:"id"`
|
||||
FullName string `json:"fullName"`
|
||||
Email *string `json:"email,omitempty"`
|
||||
Phone *string `json:"phone,omitempty"`
|
||||
ID string `json:"id"`
|
||||
FullName string `json:"fullName"`
|
||||
Email *string `json:"email,omitempty"`
|
||||
Phone *string `json:"phone,omitempty"`
|
||||
AccountRole string `json:"account_role"`
|
||||
}
|
||||
|
||||
type UserListResponse struct {
|
||||
@@ -386,35 +387,39 @@ func ListAdminUsersHandler(w http.ResponseWriter, r *http.Request) {
|
||||
searchPattern := "%" + searchTerm + "%"
|
||||
|
||||
countQuery = `
|
||||
SELECT COUNT(*)
|
||||
FROM users
|
||||
WHERE fn ILIKE $1
|
||||
OR email ILIKE $1
|
||||
OR phone ILIKE $1
|
||||
`
|
||||
SELECT COUNT(*)
|
||||
FROM users
|
||||
WHERE fn ILIKE $1
|
||||
OR email ILIKE $1
|
||||
OR phone ILIKE $1
|
||||
`
|
||||
countArgs = []interface{}{searchPattern}
|
||||
|
||||
listQuery = `
|
||||
SELECT id, fn, email, phone
|
||||
FROM users
|
||||
WHERE fn ILIKE $1
|
||||
OR email ILIKE $1
|
||||
OR phone ILIKE $1
|
||||
ORDER BY created_at DESC
|
||||
LIMIT $2 OFFSET $3
|
||||
`
|
||||
SELECT u.id, u.fn, u.email, u.phone, u.account_role
|
||||
FROM users u
|
||||
LEFT JOIN bookings b ON u.id = b.user_id
|
||||
WHERE u.fn ILIKE $1
|
||||
OR u.email ILIKE $1
|
||||
OR u.phone ILIKE $1
|
||||
GROUP BY u.id, u.fn, u.email, u.phone, u.account_role, u.created_at
|
||||
ORDER BY COUNT(b.id) DESC, u.created_at DESC
|
||||
LIMIT $2 OFFSET $3
|
||||
`
|
||||
listArgs = []interface{}{searchPattern, perPage, offset}
|
||||
} else {
|
||||
// No search - get all users
|
||||
// No search - get all users, sorted by booking count
|
||||
countQuery = `SELECT COUNT(*) FROM users`
|
||||
countArgs = []interface{}{}
|
||||
|
||||
listQuery = `
|
||||
SELECT id, fn, email, phone
|
||||
FROM users
|
||||
ORDER BY created_at DESC
|
||||
LIMIT $1 OFFSET $2
|
||||
`
|
||||
SELECT u.id, u.fn, u.email, u.phone, u.account_role
|
||||
FROM users u
|
||||
LEFT JOIN bookings b ON u.id = b.user_id
|
||||
GROUP BY u.id, u.fn, u.email, u.phone, u.account_role, u.created_at
|
||||
ORDER BY COUNT(b.id) DESC, u.created_at DESC
|
||||
LIMIT $1 OFFSET $2
|
||||
`
|
||||
listArgs = []interface{}{perPage, offset}
|
||||
}
|
||||
|
||||
@@ -439,7 +444,13 @@ func ListAdminUsersHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var users []UserListItem
|
||||
for rows.Next() {
|
||||
var user UserListItem
|
||||
err := rows.Scan(&user.ID, &user.FullName, &user.Email, &user.Phone)
|
||||
err := rows.Scan(
|
||||
&user.ID,
|
||||
&user.FullName,
|
||||
&user.Email,
|
||||
&user.Phone,
|
||||
&user.AccountRole,
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("Failed to scan user row: %v", err)
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
|
||||
Reference in New Issue
Block a user