// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial // SPDX-FileCopyrightText: 2025-2026 Christian Galo package server import ( "context" "html/template" "log/slog" "git.coopcloud.tech/wiki-cafe/member-console/internal/web" ) // The rule-less warning (design.md A8, M1): one sentence, identical on the // five surfaces that hand a rank-0 product to a population or grant one, with // the set's name linked to the set's page so the operator can add a rule // without losing the preview. It is a warning and nothing more: every commit // stays enabled and no control renders disabled. // // The condition is read from core.product_shape's active_rule_count, which is // the sole source of the answer to whether a product's set provides anything // (product-catalog; design.md M8, Decision 148). Nothing here counts // entitlement_set_rules. // ruleLessWarning returns the sentence for productID, or "" when the product // carries a set with at least one active rule, carries no set at all, or // cannot be read. A read failure is silent by design: the sentence is a // warning, and a failed read must not invent one or block the preview. func (h *OperatorPartialsHandler) ruleLessWarning(ctx context.Context, productID string) template.HTML { if productID == "" || h.BillingQ == nil || h.EntitlementsQ == nil { return "" } shape, err := h.BillingQ.GetProductShape(ctx, productID) if err != nil { h.Logger.Error("failed to read product shape for the rule-less warning", slog.Any("error", err), slog.String("product_id", productID)) return "" } if shape.ActiveRuleCount > 0 { return "" } product, err := h.BillingQ.GetProductByID(ctx, productID) if err != nil { h.Logger.Error("failed to read the product for the rule-less warning", slog.Any("error", err), slog.String("product_id", productID)) return "" } // No set at all is a different fact, reported by the readiness surfaces' // entitlement-set row; this sentence names a set. if !product.EntitlementSetID.Valid { return "" } setID := product.EntitlementSetID.UUID.String() set, err := h.EntitlementsQ.GetEntitlementSetByID(ctx, setID) if err != nil { h.Logger.Error("failed to read the entitlement set for the rule-less warning", slog.Any("error", err), slog.String("set_id", setID)) return "" } return ruleLessWarningSentence(product.Name, setID, set.Name) } // ruleLessWarningSentence is the one wording, in one place, so the five // surfaces cannot drift apart. func ruleLessWarningSentence(productName, setID, setName string) template.HTML { setURL, err := web.RouteURL("/operator/entitlement-sets/{setID}", setID) if err != nil { setURL = "/operator/entitlement-sets" } return template.HTML(template.HTMLEscapeString(productName) + ` provides nothing: its entitlement set ` + template.HTMLEscapeString(setName) + ` has no active rule.`) }