diff --git a/internal/templates/components/pages/rule_detail.templ b/internal/templates/components/pages/rule_detail.templ
index fae615f7..0634bcfc 100644
--- a/internal/templates/components/pages/rule_detail.templ
+++ b/internal/templates/components/pages/rule_detail.templ
@@ -354,31 +354,21 @@ templ ruleDetailRecentApplications(p RuleDetailProps) {
Transactions this rule has touched during syncs
if len(p.ApplicationTxns) > 0 {
-
+
for _, tx := range p.ApplicationTxns {
{{ meta := p.ApplicationMeta[tx.ID] }}
- if meta.AppliedBy != "sync" {
-
-
-
- if meta.AppliedBy == "retroactive" {
- @components.LucideIcon("rewind", "w-3 h-3")
- retroactive
- } else {
- @components.LucideIcon("wrench", "w-3 h-3")
- { meta.AppliedBy }
- }
-
-
+ if meta.AppliedBy != "" && meta.AppliedBy != "sync" {
+ @components.TxRow(tx, components.WithMetaBadge(ruleAppliedByBadge(meta.AppliedBy)))
+ } else {
+ @components.TxRow(tx)
}
- @components.TxRow(tx)
}
@@ -394,6 +384,28 @@ templ ruleDetailRecentApplications(p RuleDetailProps) {
}
+// ruleAppliedByBadge renders a compact inline meta chip describing how a
+// Recent-Applications row was touched by this rule. It rides in the TxRow
+// meta (subtitle) line via components.WithMetaBadge, replacing the old
+// floating top-right "RETROACTIVE" strip (#1917). Only non-"sync" origins
+// surface a badge — sync is the default firing path and stays unlabeled.
+// Styling matches the row's other meta chips (muted icon + text) so it reads
+// as just another subtitle detail rather than a separate banner.
+templ ruleAppliedByBadge(appliedBy string) {
+
+ if appliedBy == "retroactive" {
+ @components.LucideIcon("rewind", "w-3 h-3")
+
+ Retroactive
+ } else {
+ @components.LucideIcon("wrench", "w-3 h-3")
+ { appliedBy }
+ }
+
+}
+
// ruleDetailPendingMatches renders the "Matching transactions" card — only
// rendered when preview data is available.
//
diff --git a/internal/templates/components/pages/rule_detail_types.go b/internal/templates/components/pages/rule_detail_types.go
index f1083612..25ebfcca 100644
--- a/internal/templates/components/pages/rule_detail_types.go
+++ b/internal/templates/components/pages/rule_detail_types.go
@@ -51,6 +51,16 @@ type RuleApplicationMeta struct {
AppliedBy string
}
+// ruleAppliedByTitle returns the tooltip text for the inline meta badge that
+// marks how a Recent-Applications row was touched. Retroactive is the common
+// case; any other non-sync origin falls back to a generic phrasing.
+func ruleAppliedByTitle(appliedBy string) string {
+ if appliedBy == "retroactive" {
+ return "Applied retroactively from this rule's detail page"
+ }
+ return "Applied via " + appliedBy
+}
+
// ruleID safely returns the rule's ID, or empty string when the rule
// pointer is nil. Used to build the data-apply-url / data-toggle-url
// attributes without crashing the template render on a transient nil.
diff --git a/internal/templates/components/tx_row.templ b/internal/templates/components/tx_row.templ
index cd0b708a..41f5bfee 100644
--- a/internal/templates/components/tx_row.templ
+++ b/internal/templates/components/tx_row.templ
@@ -21,7 +21,15 @@ import (
//
// The inline category picker still reads window.__bbCategories from the
// page; pages that render tx rows must continue to populate that global.
-templ TxRow(tx service.AdminTransactionRow) {
+//
+// Optional TxRowOption values let a page embellish the row without forcing
+// the ~50 existing call sites to thread a props struct. The only option
+// today is WithMetaBadge, which renders a small chip at the tail of the
+// meta (subtitle) line — the rule-detail "Recent Applications" list uses it
+// to mark how a row was touched (e.g. "Retroactive") inline, instead of
+// floating a separate label above the row.
+templ TxRow(tx service.AdminTransactionRow, opts ...TxRowOption) {
+ {{ cfg := buildTxRowConfig(opts) }}
}
+
+ if cfg.MetaBadge != nil {
+ ·
+ @cfg.MetaBadge
+ }