// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial // SPDX-FileCopyrightText: 2025-2026 Christian Galo package forms // Shared field values (design D3; spec form-library "Two forms that write // one column share one field"). A Field written by more than one form to // the same column is declared once here and included by reference, so its // name, label, hint and rules are identical wherever it appears. A variant // is a new value with a new name, never a copy with loosened rules // (lesson L§45). // // This is what ends the "reason" collision the audit found (findings // FA-28, FA-29): Issue grant keeps "reason" for the grant-reason enum and // Extend writes its note through GrantNote, so one column has one field, // one label, one hint and one 500-character cap on both paths. // maxGrantText caps the free-text fields that feed grants.description and // pool_provision_transitions.reason. Those columns are TEXT with no // database bound; the cap is the console's own guard, applied by the // declaration so the control and the handler cannot disagree. const maxGrantText = 500 // GrantNote is the note written with a grant, by Issue grant and by Extend // alike; the text is the grant's note, the column upstream is called // description. Both are dense rows, where a line under one control lifts // its label above its neighbours', so the explanation rides the help icon // after the label rather than a hint (form-conventions "Dense form rows // keep explanations behind a help icon"; the invariants refuse a hint in a // dense form). Optional because grants.description is a nullable column // (00001_init.sql) and pre-existing deliveries carry no description at all; // an extension with no note and no expiry is a legitimate, complete // submission, not a refused one (a lesson the DB-backed suite caught before // this was corrected: TestExtendGrant_SucceedsForSinglePoolOrg and // TestGrantsListDeliveryStateAgreesWithGoDerivation both submit with the // field absent and expect success). func GrantNote() Field { return Field{ Name: "description", Label: "Note", Control: Text, Optional: true, Help: Help("Note", "Why this grant is issued. Shown with the grant in its history."), MaxLen: maxGrantText, Autocomplete: "off", } } // GrantValidUntil is the optional end of a grant, on both grant forms. // One label, because the two forms wrote one column under two names // ("Valid until" and "Expires at") before this change. func GrantValidUntil() Field { return Field{ Name: "valid_until", Label: "Valid until", Control: DateTime, Optional: true, Help: Help("Valid until", "Leave blank for a grant with no end date."), } } // RecordName is the name of a catalog record: a product, an entitlement // set, a plan ladder. Required on every one of them, with no marker, // because required is the default and only optional is marked. func RecordName() Field { return Field{ Name: "name", Label: "Name", Control: Text, MaxLen: 200, Autocomplete: "off", } } // RecordDescription is the optional prose under a catalog record's name. // The same column on three records carried three markings before this // change (finding FA-4); it carries one now. func RecordDescription() Field { return Field{ Name: "description", Label: "Description", Control: Text, Optional: true, MaxLen: 500, Autocomplete: "off", } }