diff --git a/internal/handler/auth.go b/internal/handler/auth.go index 8cb960f..42fa1a1 100644 --- a/internal/handler/auth.go +++ b/internal/handler/auth.go @@ -77,6 +77,44 @@ func (h *Handler) createSession(w http.ResponseWriter, userID string) { }) } +func (h *Handler) handleViewToggle(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + profile := getProfile(r.Context()) + if profile.Role != "owner" && profile.Role != "admin" { + http.Error(w, "Forbidden", http.StatusForbidden) + return + } + + // Toggle the cookie + current := false + if c, err := r.Cookie("view_as_buyer"); err == nil && c.Value == "1" { + current = true + } + + value := "1" + if current { + value = "" + } + http.SetCookie(w, &http.Cookie{ + Name: "view_as_buyer", + Value: value, + Path: "/", + HttpOnly: true, + SameSite: http.SameSiteLaxMode, + }) + + // Redirect back to referrer or dashboard + ref := r.Header.Get("Referer") + if ref == "" { + ref = "/" + } + http.Redirect(w, r, ref, http.StatusSeeOther) +} + func generateToken() string { b := make([]byte, 32) rand.Read(b) diff --git a/internal/handler/deals.go b/internal/handler/deals.go index 4e66aa9..0a3ab89 100644 --- a/internal/handler/deals.go +++ b/internal/handler/deals.go @@ -234,7 +234,7 @@ func (h *Handler) getRequests(dealID string, profile *model.Profile) []*model.Di query := "SELECT id, deal_id, item_number, section, description, priority, atlas_status, atlas_note, confidence, buyer_comment, seller_comment, buyer_group FROM diligence_requests WHERE deal_id = ?" args := []interface{}{dealID} - if rbac.IsBuyer(profile.Role) { + if rbac.EffectiveIsBuyer(profile) { groups := rbac.BuyerGroups(profile) if len(groups) > 0 { placeholders := make([]string, len(groups)) diff --git a/internal/handler/handler.go b/internal/handler/handler.go index d99b795..d313fb7 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -37,6 +37,7 @@ func (h *Handler) RegisterRoutes(mux *http.ServeMux) { mux.HandleFunc("/login", h.handleLoginPage) mux.HandleFunc("/auth/login", h.handleLogin) mux.HandleFunc("/auth/logout", h.handleLogout) + mux.HandleFunc("/auth/view-toggle", h.requireAuth(h.handleViewToggle)) // Pages (auth required) mux.HandleFunc("/", h.requireAuth(h.handleDashboard)) @@ -94,6 +95,13 @@ func (h *Handler) requireAuth(next http.HandlerFunc) http.HandlerFunc { return } + // Check view-as-buyer toggle (only applies to sellers) + if profile.Role == "owner" || profile.Role == "admin" { + if c, err := r.Cookie("view_as_buyer"); err == nil && c.Value == "1" { + profile.ViewAsBuyer = true + } + } + ctx := setProfile(r.Context(), &profile) next.ServeHTTP(w, r.WithContext(ctx)) } diff --git a/internal/handler/requests.go b/internal/handler/requests.go index 94f7304..63432d9 100644 --- a/internal/handler/requests.go +++ b/internal/handler/requests.go @@ -56,7 +56,7 @@ func (h *Handler) handleUpdateComment(w http.ResponseWriter, r *http.Request) { value := r.FormValue("value") field := "seller_comment" - if rbac.IsBuyer(profile.Role) { + if rbac.EffectiveIsBuyer(profile) { field = "buyer_comment" } diff --git a/internal/model/models.go b/internal/model/models.go index 2cebaba..5748f7b 100644 --- a/internal/model/models.go +++ b/internal/model/models.go @@ -31,6 +31,7 @@ type Profile struct { PasswordHash string CreatedAt time.Time LastLogin *time.Time + ViewAsBuyer bool // Not persisted, set per-request from cookie } type Deal struct { diff --git a/internal/rbac/rbac.go b/internal/rbac/rbac.go index bc33beb..7aff7dc 100644 --- a/internal/rbac/rbac.go +++ b/internal/rbac/rbac.go @@ -12,6 +12,11 @@ func IsBuyer(role string) bool { return role == "viewer" || role == "member" } +// EffectiveIsBuyer returns true if ViewAsBuyer is set OR real role is buyer +func EffectiveIsBuyer(profile *model.Profile) bool { + return profile.ViewAsBuyer || IsBuyer(profile.Role) +} + // BuyerGroups returns the buyer groups for the demo buyer func BuyerGroups(profile *model.Profile) []string { if IsBuyer(profile.Role) {