Database-Driven Badge System Design
Overview
Badge rendering in ORE Studio is currently fully hardcoded in C++. Colours live
in ColorConstants.hpp, resolver functions live in BadgeColors.hpp, and some
delegates (e.g. AccountItemDelegate) hardcode colours inline. There is no
shared language for what a badge means — no severity, no tooltip, no label. Qt
can use this, but Wt cannot, and every new badge type requires a C++ change.
Goals
- Introduce a database-driven badge system where a badge definition (label,
colours, severity, description, CSS class hint) lives in the
dqschema as reference data. - A universal mapping table links any
(code_domain, entity_code)pair to a badge definition. - Both Qt and Wt resolve badges at runtime via the standard service/protocol/client pipeline — no C++ changes needed for new badge types.
ores.codegengains awareness of badge columns so the delegate wiring is generated automatically.- All traces of the legacy hardcoded approach are removed; migration is done in clean phases with no backwards-compatibility shims.
Success Criteria
- A single DB table is the source of truth for all badge visual metadata.
- Adding a new badge type requires only a DB population entry and a codegen model annotation — no new C++ resolver functions.
- Qt and Wt use identical badge definitions loaded at startup.
- All existing badges (accounts, parties, books, DQ dimensions, FSM states, compute tasks) are migrated to the new system.
BadgeColors.hppand thebadge_colorsstruct inColorConstants.hppare deleted.
Architecture
The badge system is built entirely on existing infrastructure patterns:
- Four new domain entities in the
dqnamespace, generated byores.codegenin the normal way. - Badge definitions are reference data — loaded at client startup via the standard service/protocol/client pipeline, exactly like currencies or party statuses.
- A new
BadgeCacheclass replaces all hardcoded resolver callbacks. It is injected into delegates at construction. ores.codegengains abadgeannotation for Qt column metadata, driving delegate wiring generation.DelegatePaintUtils(pill rendering) andEntityItemDelegatestructure are unchanged.
Domain Model
Four new entities in the dq namespace.
badge_severity
Standard reference data entity. Codes align with Bootstrap 5 contextual classes so Wt rendering requires no translation layer.
| Column | Type | Notes |
|---|---|---|
code |
text | PK: secondary, info, success, warning, danger, primary |
name |
text | Display label |
description |
text | Tooltip / explanatory text |
display_order |
int |
Full codegen treatment: domain type, JSON I/O, table I/O, service, protocol, Qt management UI.
code_domain
A reusable classification registry — a named namespace that disambiguates enum
codes across entity types (e.g. ACTIVE in party_status vs ACTIVE in
book_status). Useful beyond badges for any future system needing to namespace
code values.
| Column | Type | Notes |
|---|---|---|
code |
text | PK: e.g. party_status, fsm_state, dq_nature |
name |
text | e.g. Party Status |
description |
text | |
display_order |
int |
Full codegen treatment with management UI.
badge_definition
The badge catalogue. One row per named badge.
| Column | Type | Notes |
|---|---|---|
code |
text | PK: e.g. active, locked, fsm_draft |
name |
text | Display label e.g. Active |
description |
text | Tooltip text |
background_colour |
text | Hex e.g. #22c55e |
text_colour |
text | Hex e.g. #ffffff |
severity_code |
text | Soft FK → badge_severity |
css_class |
text | Nullable; Bootstrap hint for Wt e.g. badge bg-success |
display_order |
int |
Full codegen treatment with management UI. css_class is nullable — unused by
Qt, consumed by Wt when present, extensible for future CSS frameworks.
badge_mapping
Universal link table. Maps any (code_domain, entity_code) pair to a badge.
| Column | Type | Notes |
|---|---|---|
code_domain_code |
text | Soft FK → code_domain |
entity_code |
text | e.g. ACTIVE, DRAFT |
badge_code |
text | Soft FK → badge_definition |
PK: (tenant_id, code_domain_code, entity_code, valid_from). Temporal,
tenant-scoped. Junction-style codegen: SQL + C++ domain type + repository.
No management UI — populated via seed scripts.
Data Flow
Badge definitions follow the identical path as every other reference data type.
Startup Sequence
- Qt/Wt client connects and authenticates.
- Client requests reference data from the service layer. Badge severities, code domains, badge definitions, and badge mappings are loaded as part of this initial fetch.
- A new
BadgeCacheclass holds the loaded data and exposes a single lookup:
std::optional<badge_definition> resolve(std::string_view code_domain, std::string_view entity_code) const;
- All Qt delegates that currently use
badge_color_resolvercallbacks are replaced withBadgeCache::resolve(). The cache is injected at construction.
Render Time — Qt
- Delegate calls
cache.resolve("party_status", "ACTIVE"). - Gets back a
badge_definitionwithbackground_colour,text_colour,name,severity_code. - Passes colours to the existing
draw_centered_badge()/draw_inline_badge()inDelegatePaintUtils— those functions do not change. - If no mapping is found, falls back to the existing default grey.
Render Time — Wt
- Same
cache.resolve()call. - Uses
css_classfrom the definition if present (e.g.badge bg-success), otherwise constructs an inline style frombackground_colour/text_colour.
Codegen Changes
New Models
Four new JSON model files in models/dq/:
badge_severity_domain_entity.jsoncode_domain_domain_entity.jsonbadge_definition_domain_entity.jsonbadge_mapping_junction.json
Plus a population seed file (badge_definitions_populate.json) encoding all
current hardcoded colours from BadgeColors.hpp and ColorConstants.hpp as
initial data.
Badge Column Annotation
The Qt column metadata in domain entity JSON gains an optional badge object:
{
"field": "status_code",
"header": "Status",
"width": 100,
"is_string": true,
"badge": {
"code_domain": "party_status"
}
}
When codegen sees badge on a column it:
- Sets the column style to
badge_centered(already exists). - Generates delegate wiring that calls
BadgeCache::resolve("party_status", value)instead of a hardcoded resolver callback. - Emits no resolver function.
Adding a badge column to a new entity type in the future is fully declarative: annotate the model, run codegen, done.
Migration
Migration is done in three clean phases. Each phase produces a PR that leaves the codebase strictly cleaner than before. No backwards-compatibility shims — legacy code is removed as each phase completes.
Phase 1 — Infrastructure
- Add the four new DB tables via codegen-generated SQL.
- Populate
badge_severities,code_domains,badge_definitions, andbadge_mappingsfrom the current hardcoded values inBadgeColors.hppandColorConstants.hpp. - Implement
BadgeCacheand wire it into the service/protocol/client startup sequence. - Existing delegates still use the old resolver callbacks during this phase.
Phase 2 — Delegate Migration
Migrate delegates one by one, annotating each codegen model and regenerating:
- Simple status delegates sharing
resolve_status_badge_color: parties, books, business units, counterparties. - Specialised delegates: portfolios, compute tasks, report definitions.
AccountItemDelegate— inline hardcoded colours, worst offender.DatasetItemDelegate— multi-badge cells (Origin + Nature + Treatment), most structurally different.
Phase 3 — Cleanup
- Delete
BadgeColors.hpp. - Remove
badge_colorsstruct fromColorConstants.hpp. - Remove
badge_color_resolvercallback fromEntityItemDelegate. - Remove all individual resolver functions.
Testing Strategy
Unit Tests (Catch2)
BadgeCache: table-driven tests forresolve()with known mappings, missing mapping returns empty optional, case handling.badge_definitiondomain type: standard JSON round-trip and table I/O tests, same pattern as every other domain entity.
Integration Tests
- Badge definitions and mappings load correctly via the service layer on startup.
- Seed data completeness: every
(code_domain, entity_code)pair previously hardcoded inBadgeColors.hpphas a corresponding mapping entry — asserted by comparing the seed file against the old resolver logic during migration.
Open Questions
- Should
BadgeCachebe a standalone component or merged into the existing reference data cache? To be decided during Phase 1 implementation. - Wt badge rendering will need a dedicated widget/utility equivalent to
DelegatePaintUtils. Scope to be defined when Wt work begins.