Implement the ux-first-run change: a state-derived setup checklist on /operator/setup with a landing region that recedes once required steps are done, and empty states that distinguish blocked from empty across operator and member surfaces. Also add production deployment and environment reference docs, plus a config-key completeness test.
568 lines
16 KiB
Go
568 lines
16 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.29.0
|
|
// source: plan_ladders.sql
|
|
|
|
package billing
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
const anyRankZeroTier = `-- name: AnyRankZeroTier :one
|
|
SELECT EXISTS(SELECT 1 FROM core.plan_ladder_tiers WHERE rank = 0)
|
|
`
|
|
|
|
// Setup-checklist existence probe: a ladder delivers only through its rank-0
|
|
// tier, so "ladder exists" alone is not the step.
|
|
func (q *Queries) AnyRankZeroTier(ctx context.Context) (bool, error) {
|
|
row := q.db.QueryRowContext(ctx, anyRankZeroTier)
|
|
var exists bool
|
|
err := row.Scan(&exists)
|
|
return exists, err
|
|
}
|
|
|
|
const createPlanLadder = `-- name: CreatePlanLadder :one
|
|
INSERT INTO core.plan_ladders (ladder_key, name, description, is_active)
|
|
VALUES ($1, $2, $3, $4)
|
|
RETURNING plan_ladder_id, ladder_key, name, description, is_active, created_at, updated_at, sort_order
|
|
`
|
|
|
|
type CreatePlanLadderParams struct {
|
|
LadderKey string `json:"ladder_key"`
|
|
Name string `json:"name"`
|
|
Description sql.NullString `json:"description"`
|
|
IsActive bool `json:"is_active"`
|
|
}
|
|
|
|
func (q *Queries) CreatePlanLadder(ctx context.Context, arg CreatePlanLadderParams) (PlanLadder, error) {
|
|
row := q.db.QueryRowContext(ctx, createPlanLadder,
|
|
arg.LadderKey,
|
|
arg.Name,
|
|
arg.Description,
|
|
arg.IsActive,
|
|
)
|
|
var i PlanLadder
|
|
err := row.Scan(
|
|
&i.PlanLadderID,
|
|
&i.LadderKey,
|
|
&i.Name,
|
|
&i.Description,
|
|
&i.IsActive,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.SortOrder,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const createPlanLadderTier = `-- name: CreatePlanLadderTier :one
|
|
INSERT INTO core.plan_ladder_tiers (plan_ladder_id, product_id, rank)
|
|
VALUES ($1, $2, (
|
|
SELECT COALESCE(MAX(rank) + 1, 0)
|
|
FROM core.plan_ladder_tiers
|
|
WHERE plan_ladder_id = $1
|
|
))
|
|
RETURNING plan_ladder_id, product_id, rank, created_at, updated_at
|
|
`
|
|
|
|
type CreatePlanLadderTierParams struct {
|
|
PlanLadderID string `json:"plan_ladder_id"`
|
|
ProductID string `json:"product_id"`
|
|
}
|
|
|
|
// Appends the tier at the end of the ladder: rank self-assigns to MAX+1 in the
|
|
// INSERT itself (no read-then-write race). Reorder keeps ranks contiguous
|
|
// 0..N-1, so MAX+1 == COUNT; operators position tiers by drag-and-drop.
|
|
func (q *Queries) CreatePlanLadderTier(ctx context.Context, arg CreatePlanLadderTierParams) (PlanLadderTier, error) {
|
|
row := q.db.QueryRowContext(ctx, createPlanLadderTier, arg.PlanLadderID, arg.ProductID)
|
|
var i PlanLadderTier
|
|
err := row.Scan(
|
|
&i.PlanLadderID,
|
|
&i.ProductID,
|
|
&i.Rank,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const deletePlanLadder = `-- name: DeletePlanLadder :exec
|
|
DELETE FROM core.plan_ladders
|
|
WHERE plan_ladder_id = $1
|
|
`
|
|
|
|
func (q *Queries) DeletePlanLadder(ctx context.Context, planLadderID string) error {
|
|
_, err := q.db.ExecContext(ctx, deletePlanLadder, planLadderID)
|
|
return err
|
|
}
|
|
|
|
const deleteTier = `-- name: DeleteTier :exec
|
|
DELETE FROM core.plan_ladder_tiers
|
|
WHERE plan_ladder_id = $1 AND product_id = $2
|
|
`
|
|
|
|
type DeleteTierParams struct {
|
|
PlanLadderID string `json:"plan_ladder_id"`
|
|
ProductID string `json:"product_id"`
|
|
}
|
|
|
|
func (q *Queries) DeleteTier(ctx context.Context, arg DeleteTierParams) error {
|
|
_, err := q.db.ExecContext(ctx, deleteTier, arg.PlanLadderID, arg.ProductID)
|
|
return err
|
|
}
|
|
|
|
const getPlanLadderByID = `-- name: GetPlanLadderByID :one
|
|
SELECT plan_ladder_id, ladder_key, name, description, is_active, created_at, updated_at, sort_order FROM core.plan_ladders
|
|
WHERE plan_ladder_id = $1
|
|
`
|
|
|
|
func (q *Queries) GetPlanLadderByID(ctx context.Context, planLadderID string) (PlanLadder, error) {
|
|
row := q.db.QueryRowContext(ctx, getPlanLadderByID, planLadderID)
|
|
var i PlanLadder
|
|
err := row.Scan(
|
|
&i.PlanLadderID,
|
|
&i.LadderKey,
|
|
&i.Name,
|
|
&i.Description,
|
|
&i.IsActive,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.SortOrder,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getPlanLadderByKey = `-- name: GetPlanLadderByKey :one
|
|
SELECT plan_ladder_id, ladder_key, name, description, is_active, created_at, updated_at, sort_order FROM core.plan_ladders
|
|
WHERE ladder_key = $1
|
|
`
|
|
|
|
func (q *Queries) GetPlanLadderByKey(ctx context.Context, ladderKey string) (PlanLadder, error) {
|
|
row := q.db.QueryRowContext(ctx, getPlanLadderByKey, ladderKey)
|
|
var i PlanLadder
|
|
err := row.Scan(
|
|
&i.PlanLadderID,
|
|
&i.LadderKey,
|
|
&i.Name,
|
|
&i.Description,
|
|
&i.IsActive,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.SortOrder,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getTier = `-- name: GetTier :one
|
|
SELECT plan_ladder_id, product_id, rank, created_at, updated_at FROM core.plan_ladder_tiers
|
|
WHERE plan_ladder_id = $1 AND product_id = $2
|
|
`
|
|
|
|
type GetTierParams struct {
|
|
PlanLadderID string `json:"plan_ladder_id"`
|
|
ProductID string `json:"product_id"`
|
|
}
|
|
|
|
func (q *Queries) GetTier(ctx context.Context, arg GetTierParams) (PlanLadderTier, error) {
|
|
row := q.db.QueryRowContext(ctx, getTier, arg.PlanLadderID, arg.ProductID)
|
|
var i PlanLadderTier
|
|
err := row.Scan(
|
|
&i.PlanLadderID,
|
|
&i.ProductID,
|
|
&i.Rank,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getTierByLadderRank = `-- name: GetTierByLadderRank :one
|
|
SELECT plan_ladder_id, product_id, rank, created_at, updated_at FROM core.plan_ladder_tiers
|
|
WHERE plan_ladder_id = $1 AND rank = $2
|
|
`
|
|
|
|
type GetTierByLadderRankParams struct {
|
|
PlanLadderID string `json:"plan_ladder_id"`
|
|
Rank int32 `json:"rank"`
|
|
}
|
|
|
|
func (q *Queries) GetTierByLadderRank(ctx context.Context, arg GetTierByLadderRankParams) (PlanLadderTier, error) {
|
|
row := q.db.QueryRowContext(ctx, getTierByLadderRank, arg.PlanLadderID, arg.Rank)
|
|
var i PlanLadderTier
|
|
err := row.Scan(
|
|
&i.PlanLadderID,
|
|
&i.ProductID,
|
|
&i.Rank,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const listLaddersByProduct = `-- name: ListLaddersByProduct :many
|
|
SELECT t.plan_ladder_id, t.product_id, t.rank, t.created_at, t.updated_at, l.ladder_key, l.name AS ladder_name
|
|
FROM core.plan_ladder_tiers t
|
|
JOIN core.plan_ladders l ON l.plan_ladder_id = t.plan_ladder_id
|
|
WHERE t.product_id = $1
|
|
ORDER BY l.ladder_key ASC
|
|
`
|
|
|
|
type ListLaddersByProductRow struct {
|
|
PlanLadderID string `json:"plan_ladder_id"`
|
|
ProductID string `json:"product_id"`
|
|
Rank int32 `json:"rank"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
LadderKey string `json:"ladder_key"`
|
|
LadderName string `json:"ladder_name"`
|
|
}
|
|
|
|
func (q *Queries) ListLaddersByProduct(ctx context.Context, productID string) ([]ListLaddersByProductRow, error) {
|
|
rows, err := q.db.QueryContext(ctx, listLaddersByProduct, productID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []ListLaddersByProductRow{}
|
|
for rows.Next() {
|
|
var i ListLaddersByProductRow
|
|
if err := rows.Scan(
|
|
&i.PlanLadderID,
|
|
&i.ProductID,
|
|
&i.Rank,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.LadderKey,
|
|
&i.LadderName,
|
|
); 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 listPlanLadders = `-- name: ListPlanLadders :many
|
|
SELECT plan_ladder_id, ladder_key, name, description, is_active, created_at, updated_at, sort_order FROM core.plan_ladders
|
|
ORDER BY sort_order ASC, name ASC
|
|
`
|
|
|
|
func (q *Queries) ListPlanLadders(ctx context.Context) ([]PlanLadder, error) {
|
|
rows, err := q.db.QueryContext(ctx, listPlanLadders)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []PlanLadder{}
|
|
for rows.Next() {
|
|
var i PlanLadder
|
|
if err := rows.Scan(
|
|
&i.PlanLadderID,
|
|
&i.LadderKey,
|
|
&i.Name,
|
|
&i.Description,
|
|
&i.IsActive,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.SortOrder,
|
|
); 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 listPlanLaddersWithRankZeroProduct = `-- name: ListPlanLaddersWithRankZeroProduct :many
|
|
SELECT l.plan_ladder_id, l.ladder_key, l.name AS ladder_name, l.is_active,
|
|
t.product_id, p.name AS rank_zero_product_name
|
|
FROM core.plan_ladders l
|
|
LEFT JOIN core.plan_ladder_tiers t
|
|
ON t.plan_ladder_id = l.plan_ladder_id AND t.rank = 0
|
|
LEFT JOIN core.products p ON p.product_id = t.product_id
|
|
ORDER BY l.ladder_key ASC
|
|
`
|
|
|
|
type ListPlanLaddersWithRankZeroProductRow struct {
|
|
PlanLadderID string `json:"plan_ladder_id"`
|
|
LadderKey string `json:"ladder_key"`
|
|
LadderName string `json:"ladder_name"`
|
|
IsActive bool `json:"is_active"`
|
|
ProductID uuid.NullUUID `json:"product_id"`
|
|
RankZeroProductName sql.NullString `json:"rank_zero_product_name"`
|
|
}
|
|
|
|
// LEFT JOINs on purpose: a ladder whose rank-0 tier was deleted must still
|
|
// appear (product columns NULL) so surfaces that reference it — notably an
|
|
// org type's configured default — can show it as broken instead of silently
|
|
// dropping it from a dropdown and NULLing the stored value on save.
|
|
func (q *Queries) ListPlanLaddersWithRankZeroProduct(ctx context.Context) ([]ListPlanLaddersWithRankZeroProductRow, error) {
|
|
rows, err := q.db.QueryContext(ctx, listPlanLaddersWithRankZeroProduct)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []ListPlanLaddersWithRankZeroProductRow{}
|
|
for rows.Next() {
|
|
var i ListPlanLaddersWithRankZeroProductRow
|
|
if err := rows.Scan(
|
|
&i.PlanLadderID,
|
|
&i.LadderKey,
|
|
&i.LadderName,
|
|
&i.IsActive,
|
|
&i.ProductID,
|
|
&i.RankZeroProductName,
|
|
); 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 listSharedTierProducts = `-- name: ListSharedTierProducts :many
|
|
SELECT t.product_id, p.name AS product_name,
|
|
t.plan_ladder_id, l.ladder_key, l.name AS ladder_name, t.rank
|
|
FROM core.plan_ladder_tiers t
|
|
JOIN core.products p ON p.product_id = t.product_id
|
|
JOIN core.plan_ladders l ON l.plan_ladder_id = t.plan_ladder_id
|
|
WHERE t.product_id IN (
|
|
SELECT product_id FROM core.plan_ladder_tiers
|
|
GROUP BY product_id
|
|
HAVING COUNT(*) > 1
|
|
)
|
|
ORDER BY p.name ASC, l.sort_order ASC, l.name ASC
|
|
`
|
|
|
|
type ListSharedTierProductsRow struct {
|
|
ProductID string `json:"product_id"`
|
|
ProductName string `json:"product_name"`
|
|
PlanLadderID string `json:"plan_ladder_id"`
|
|
LadderKey string `json:"ladder_key"`
|
|
LadderName string `json:"ladder_name"`
|
|
Rank int32 `json:"rank"`
|
|
}
|
|
|
|
// Products that are a tier in more than one ladder (the many-to-many made
|
|
// legible for the operator topology overview). Returns one row per
|
|
// (shared product, ladder) membership, ordered for stable grouping in Go.
|
|
func (q *Queries) ListSharedTierProducts(ctx context.Context) ([]ListSharedTierProductsRow, error) {
|
|
rows, err := q.db.QueryContext(ctx, listSharedTierProducts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []ListSharedTierProductsRow{}
|
|
for rows.Next() {
|
|
var i ListSharedTierProductsRow
|
|
if err := rows.Scan(
|
|
&i.ProductID,
|
|
&i.ProductName,
|
|
&i.PlanLadderID,
|
|
&i.LadderKey,
|
|
&i.LadderName,
|
|
&i.Rank,
|
|
); 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 listTiersByLadder = `-- name: ListTiersByLadder :many
|
|
SELECT plan_ladder_id, product_id, rank, created_at, updated_at FROM core.plan_ladder_tiers
|
|
WHERE plan_ladder_id = $1
|
|
ORDER BY rank ASC
|
|
`
|
|
|
|
func (q *Queries) ListTiersByLadder(ctx context.Context, planLadderID string) ([]PlanLadderTier, error) {
|
|
rows, err := q.db.QueryContext(ctx, listTiersByLadder, planLadderID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []PlanLadderTier{}
|
|
for rows.Next() {
|
|
var i PlanLadderTier
|
|
if err := rows.Scan(
|
|
&i.PlanLadderID,
|
|
&i.ProductID,
|
|
&i.Rank,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
); 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 listTiersByLadderWithProducts = `-- name: ListTiersByLadderWithProducts :many
|
|
SELECT t.plan_ladder_id, t.product_id, t.rank, t.created_at, t.updated_at,
|
|
p.name AS product_name, p.display_category, p.lifecycle_status
|
|
FROM core.plan_ladder_tiers t
|
|
JOIN core.products p ON p.product_id = t.product_id
|
|
WHERE t.plan_ladder_id = $1
|
|
ORDER BY t.rank ASC
|
|
`
|
|
|
|
type ListTiersByLadderWithProductsRow struct {
|
|
PlanLadderID string `json:"plan_ladder_id"`
|
|
ProductID string `json:"product_id"`
|
|
Rank int32 `json:"rank"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
ProductName string `json:"product_name"`
|
|
DisplayCategory sql.NullString `json:"display_category"`
|
|
LifecycleStatus string `json:"lifecycle_status"`
|
|
}
|
|
|
|
func (q *Queries) ListTiersByLadderWithProducts(ctx context.Context, planLadderID string) ([]ListTiersByLadderWithProductsRow, error) {
|
|
rows, err := q.db.QueryContext(ctx, listTiersByLadderWithProducts, planLadderID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []ListTiersByLadderWithProductsRow{}
|
|
for rows.Next() {
|
|
var i ListTiersByLadderWithProductsRow
|
|
if err := rows.Scan(
|
|
&i.PlanLadderID,
|
|
&i.ProductID,
|
|
&i.Rank,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.ProductName,
|
|
&i.DisplayCategory,
|
|
&i.LifecycleStatus,
|
|
); 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 setPlanLadderSortOrder = `-- name: SetPlanLadderSortOrder :exec
|
|
UPDATE core.plan_ladders
|
|
SET sort_order = $2
|
|
WHERE plan_ladder_id = $1
|
|
`
|
|
|
|
type SetPlanLadderSortOrderParams struct {
|
|
PlanLadderID string `json:"plan_ladder_id"`
|
|
SortOrder int32 `json:"sort_order"`
|
|
}
|
|
|
|
// Sets a single ladder's display order. Used by the topology reorder flow,
|
|
// which renumbers ladders to a contiguous sequence; sort_order is deliberately
|
|
// NOT writable via UpdatePlanLadder so editing a ladder never reorders it.
|
|
func (q *Queries) SetPlanLadderSortOrder(ctx context.Context, arg SetPlanLadderSortOrderParams) error {
|
|
_, err := q.db.ExecContext(ctx, setPlanLadderSortOrder, arg.PlanLadderID, arg.SortOrder)
|
|
return err
|
|
}
|
|
|
|
const updatePlanLadder = `-- name: UpdatePlanLadder :one
|
|
UPDATE core.plan_ladders
|
|
SET name = $2, description = $3, is_active = $4
|
|
WHERE plan_ladder_id = $1
|
|
RETURNING plan_ladder_id, ladder_key, name, description, is_active, created_at, updated_at, sort_order
|
|
`
|
|
|
|
type UpdatePlanLadderParams struct {
|
|
PlanLadderID string `json:"plan_ladder_id"`
|
|
Name string `json:"name"`
|
|
Description sql.NullString `json:"description"`
|
|
IsActive bool `json:"is_active"`
|
|
}
|
|
|
|
func (q *Queries) UpdatePlanLadder(ctx context.Context, arg UpdatePlanLadderParams) (PlanLadder, error) {
|
|
row := q.db.QueryRowContext(ctx, updatePlanLadder,
|
|
arg.PlanLadderID,
|
|
arg.Name,
|
|
arg.Description,
|
|
arg.IsActive,
|
|
)
|
|
var i PlanLadder
|
|
err := row.Scan(
|
|
&i.PlanLadderID,
|
|
&i.LadderKey,
|
|
&i.Name,
|
|
&i.Description,
|
|
&i.IsActive,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.SortOrder,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const updateTierRank = `-- name: UpdateTierRank :one
|
|
UPDATE core.plan_ladder_tiers
|
|
SET rank = $3
|
|
WHERE plan_ladder_id = $1 AND product_id = $2
|
|
RETURNING plan_ladder_id, product_id, rank, created_at, updated_at
|
|
`
|
|
|
|
type UpdateTierRankParams struct {
|
|
PlanLadderID string `json:"plan_ladder_id"`
|
|
ProductID string `json:"product_id"`
|
|
Rank int32 `json:"rank"`
|
|
}
|
|
|
|
func (q *Queries) UpdateTierRank(ctx context.Context, arg UpdateTierRankParams) (PlanLadderTier, error) {
|
|
row := q.db.QueryRowContext(ctx, updateTierRank, arg.PlanLadderID, arg.ProductID, arg.Rank)
|
|
var i PlanLadderTier
|
|
err := row.Scan(
|
|
&i.PlanLadderID,
|
|
&i.ProductID,
|
|
&i.Rank,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|