feat: Allow updation of award catalog

This commit is contained in:
2026-07-14 16:22:54 +02:00
parent 1f0f75baf8
commit 4fe0405432
6 changed files with 188 additions and 2 deletions
+97
View File
@@ -2472,6 +2472,103 @@ func (a *App) mirrorAwardsToFolder(defs []award.Def) {
}
}
// AwardUpdate is a shipped fix an operator has NOT received, because they edited
// the award and we refuse to overwrite their work. Rather than drop the fix
// silently, we offer it: applying is their call, and it costs them their changes.
type AwardUpdate struct {
Code string `json:"code"`
Name string `json:"name"` // the catalog's name — theirs may differ
From int `json:"from"` // version they run
To int `json:"to"` // version we ship
}
// GetAwardUpdates lists the catalog fixes waiting on an operator's decision.
// Awards they have NOT edited are updated automatically at startup and never
// appear here.
func (a *App) GetAwardUpdates() []AwardUpdate {
out := []AwardUpdate{}
stored := map[string]award.Def{}
for _, d := range a.awardDefs() {
stored[strings.ToUpper(strings.TrimSpace(d.Code))] = d
}
for _, c := range award.Defaults() {
s, ok := stored[strings.ToUpper(strings.TrimSpace(c.Code))]
if !ok || !s.UserEdited || c.Version <= s.Version {
continue
}
out = append(out, AwardUpdate{Code: c.Code, Name: c.Name, From: s.Version, To: c.Version})
}
return out
}
// ApplyAwardUpdate takes the catalog's version of an award, DISCARDING the
// operator's changes to it — definition and reference list both. Only ever called
// because they clicked through a warning saying exactly that.
func (a *App) ApplyAwardUpdate(code string) error {
if a.settings == nil {
return fmt.Errorf("db not initialized")
}
var fresh award.Def
for _, c := range award.Defaults() {
if strings.EqualFold(strings.TrimSpace(c.Code), strings.TrimSpace(code)) {
fresh = c
}
}
if fresh.Code == "" {
return fmt.Errorf("%s is not an award OpsLog ships", code)
}
defs := a.awardDefs()
found := false
for i := range defs {
if !strings.EqualFold(strings.TrimSpace(defs[i].Code), strings.TrimSpace(code)) {
continue
}
fresh.Valid = defs[i].Valid // still their choice whether it is switched on
defs[i] = fresh // UserEdited cleared: it is the catalog's award again
found = true
}
if !found {
return fmt.Errorf("%s not found", code)
}
b, err := json.Marshal(defs)
if err != nil {
return err
}
if err := a.settings.SetGlobal(a.ctx, keyAwardDefs, string(b)); err != nil {
return err
}
a.reseedRefsFromCatalog(fresh.Code)
a.mirrorAwards()
applog.Printf("awards: %s updated to catalog v%d at the operator's request", fresh.Code, fresh.Version)
return nil
}
// DismissAwardUpdate keeps the operator's award as it is and stops offering this
// fix. It records the version they turned down — not the content — so the award
// stays theirs, and a LATER revision is still offered.
func (a *App) DismissAwardUpdate(code string) error {
if a.settings == nil {
return fmt.Errorf("db not initialized")
}
var v int
for _, c := range award.Defaults() {
if strings.EqualFold(strings.TrimSpace(c.Code), strings.TrimSpace(code)) {
v = c.Version
}
}
defs := a.awardDefs()
for i := range defs {
if strings.EqualFold(strings.TrimSpace(defs[i].Code), strings.TrimSpace(code)) {
defs[i].Version = v
}
}
b, err := json.Marshal(defs)
if err != nil {
return err
}
return a.settings.SetGlobal(a.ctx, keyAwardDefs, string(b))
}
// ResetAwardDefs restores the built-in defaults.
func (a *App) ResetAwardDefs() ([]award.Def, error) {
if a.settings == nil {