Appearance
GUI Labelling
Includes UI Field Hints (Confluence page "UI Field Hints", id 582156302), which the source material states was merged into GUI Labelling: "This feature is implemented as an extension of GUI Labelling (see
uiLabel.hint). The two dedicated endpoints originally proposed [for a standalone hints feature] are NOT being built; hint text is served and edited via the existing GUI Labelling API and admin screen." Field-hint content is folded into the relevant sections below rather than kept as a separate document.
Business Context
Business-Level Definition
GUI Labelling gives a superadmin the ability to change any piece of UI text shown across the E-Manazer application — form labels, field names, section titles, button captions and similar display strings — without requiring a developer or a deployment.
A change is global: it applies identically to every tenant immediately. The data structure is prepared for multiple languages (CZ / SK / EN) from day one, even though only Czech (cs) is populated and exposed in the first phase (v1).
This is deliberately not a per-tenant customisation feature. A label change is a system-wide correction or improvement, not a way for individual tenants to brand or personalise their own instance.
Users across EM3 also need contextual guidance when filling in form fields — especially for less obvious attributes such as technical contacts, distributor flags, or energy-related identifiers. This is provided as an optional hint attached to any UI label (an ℹ️ tooltip next to the field label), managed through the same mechanism as the label itself rather than as a separate feature.
Requirements Definition
GUI Labelling must support:
- Editing the displayed text of any UI label, globally, without a deployment
- Restricting who can make this change to a dedicated superadmin role (above any tenant-level admin role)
- Resetting an edited label back to its original ("factory") text, per language, at any time
- Preparing the data structure for CZ / SK / EN from the start, while only implementing and exposing CZ in v1
- Filtering the label list in the admin UI by application area — e.g. a superadmin can find all labels related to "Gauges" without having to scroll through all/unrelated entries
- Attaching an optional, admin-manageable hint to any UI label, shown as an ℹ️ icon next to the field, that a user can view without a developer or deployment being involved. Hints are optional and non-intrusive — a field with no hint set shows no icon.
Acceptance Criteria
- A superadmin can view, search and filter all UI labels by application area (component) and by label type
- Only a superadmin can edit the currently displayed text (
currentValue) of any label for any implemented locale; the change is visible in the UI immediately, for all tenants - Only a superadmin can reset a label back to its original text (
defaultValue) for a specific locale; resetting one locale does not affect other locales of the same label - The system stores exactly the default value and the current value per label per locale — no intermediate version history is required or kept
- Adding a new locale (e.g.
sk) in the future does not require a schema change — only newuiLabelrows for that locale - A label key that has no
uiLabelrecord for the tenant-facing locale falls back to a defined behaviour (see Functional Requirements → "Missing label fallback") - A
currentValuecannot be saved empty or whitespace-only for any locale hintremains optional and can still be cleared- All field hints from EM2 (see Backward Compatibility and Migration) are available in EM3 for the same fields, where they existed
- A field with a hint set shows the ℹ️ icon; clicking/hovering displays the hint text; a field with no hint shows no icon
- A user with
showUiHints = falsesees no ℹ️ icons anywhere, even where hint text is set for the field/locale
Loom Link
N/A — not available in the source material.
Technical Context
User Stories / Use Cases
Browse and search labels: A superadmin opens the label management screen → sees a list of all label keys, grouped or filterable by uiLabelComponent and uiLabelType → each row shows the key, the default value and the current value for the active locale.
Edit a label: A superadmin selects a label → edits the currentValue for one or more locales → saves. The change takes effect immediately across all tenants; no deployment or cache warm-up delay is acceptable beyond normal cache TTL (see Caching).
Reset a label: A superadmin opens an edited label → sees both the default and current value side by side for each locale → clicks "Reset" on a specific locale → currentValue is overwritten with defaultValue for that locale only. Other locales of the same label are unaffected.
Developer introduces a new label: A developer adds a new UI string in the codebase, referencing a new translationKey. As part of the release, a uiLabel seed record is created for that key (see Data and Backward Compatibility and Migration below) with defaultValue = currentValue = the text the developer wrote. The label is now visible and editable in the superadmin screen without further backend work.
User sees a field hint: A user with showUiHints = true (the default) hovers or clicks the ℹ️ icon next to a field that has a hint set → the hint text is displayed. A user with showUiHints = false never sees the icon, regardless of whether a hint is set.
Admin edits a field hint: A superadmin opens the same GUI Labelling admin screen used for labels, selects a translationKey, and edits its hint for one or more locales — no separate screen, entity or endpoint is used for this.
UI/UX Design
Pending UI/UX review. Acceptance criteria for this feature describe functional behaviour only. Visual and interaction design will be added once wireframes are approved.
Functional Requirements
Default value vs. current value
Every label carries exactly two values per locale:
uiLabel.defaultValue— the original text as shipped by development. Set only at seed/deployment time; not editable (not even in the superadmin UI)uiLabel.currentValue— the text actually rendered in the UI. Editable by a superadmin. Initialised equal todefaultValueat seed time.
No intermediate history between these two states is kept. A "reset" is simply currentValue := defaultValue for the given (translationKey, locale).
A uiLabel record has no tenantId — a change made by a superadmin is visible to every tenant immediately.
Key naming and categorisation
Each label is addressed by a translationKey in dot notation, namespaced by area for readability, e.g.: gauge.form.levelLabel, building.detail.title, shared.button.save.
Two additional attributes support the admin UI but carry no runtime behaviour:
uiLabelComponent— which application area the label belongs to (mirrors the top-level Domain Model areas, plusshared)uiLabelType— what kind of text it is (fixed static text, a specific form field's label, or a label tied to an enum-value combination elsewhere in the domain model)
Superadmin search/filter is expected to combine both: component narrows the list to a domain area, and free-text search on uiLabel.translationKey pinpoints the exact screen.
Superadmin-only editing
Editing currentValue requires a dedicated superadmin role. TBD in the Users & Access feature page (until then, this feature refers to it functionally as "superadmin").
Missing label fallback
If the UI requests a label for a (translationKey, locale) combination that has no uiLabel record (e.g. a newly introduced key not yet seeded, or a locale not yet populated), the system falls back in this order:
currentValuefor the requested locale, if a record existscurrentValuefor thecslocale (the baseline locale in phase 1), if the requested locale has no record- The raw
translationKeyitself, rendered as a visible fallback string, if no record exists for the key in any locale
Field hint (merged from UI Field Hints)
Each uiLabel.hint is an optional, single-value text per (translationKey, locale), shown in the UI as an ℹ️ icon next to the field label; clicking/hovering the icon displays the hint text.
hinthas no default/current split — a superadmin edits it directly, and it is not affected by the "Reset" action.- if
hintis null/empty for the requested locale, no icon is shown — there is no fallback to another locale's hint - the hint icon ℹ️ is rendered only when a per-user preference is active —
userPreferencekeyui.showUiHints(default true; per tenant, not per platform account — see JSON-DAT: userPreference.showUiHintsValue) - managed through the same GUI Labelling admin screen and API as the label itself — no separate screen, entity or endpoints
- hint text is optional on every user-facing field; hint editing happens on the same admin screen as label editing
- the per-user
showUiHintspreference is a display-only toggle, independent of the hint content/editing mechanism described above
Origin of this requirement: raised via inline comment on the organisation entity page regarding the contactTechnicalUserId attribute.
Current value validation
A currentValue must always contain non-empty text — saving an empty or whitespace-only value is rejected, both in the superadmin UI and at the API level (PATCH /v1/admin/ui-labels/:translationKey). hint is exempt from this rule: it remains optional and can be cleared to empty at any time.
Non-Functional Requirements
Performance
- Label reads must be served from cache — the full active-locale label set is small enough to load once per session/app-shell load, not per-request
- Cache must be invalidated immediately (not on a TTL delay) whenever a superadmin edits or resets a label, so changes are visible without requiring users to hard-refresh beyond normal navigation
Caching
- All
currentValuelookups for the active locale are cached in-memory at the application layer, keyed by locale - Cache invalidation is triggered synchronously on every write (edit or reset) — see Implementation Notes
Transactional Operations
- A label edit is a single-row update; no multi-table transaction is required
- Seeding a new label (
defaultValue=currentValueat creation) must not overwrite an existing record for the same(translationKey, locale)— seeding is insert-only for keys that do not yet exist
API Analysis
GET /v1/admin/ui-labels List labels (superadmin only; filterable by component, type, locale)
GET /v1/admin/ui-labels/:translationKey Label detail across all locales
PATCH /v1/admin/ui-labels/:translationKey Update currentValue for one or more locales (superadmin only)
POST /v1/admin/ui-labels/:translationKey/reset Reset currentValue to defaultValue for a specific locale (superadmin only)
GET /v1/ui-labels?locale=:locale Public, cached read endpoint used by the frontend to load the active label sethint is carried by the same endpoints above (list/detail response, updatable via the existing PATCH, exposed on the public read endpoint) — no dedicated hint endpoints or entity, per the merge decision described at the top of this document.
Domain Model (ER diagram) & Data Attribute Table
No ER diagram in source material. Related entity pages (Confluence, not yet migrated into this repo's Entity DAT catalog):
- uiLabel — default and current UI label text per locale, plus the optional per-locale
hint - userPreference — the generic per-user, per-tenant settings table (introduced by Table View Settings); carries
showUiHintsunder keyui.showUiHints(Users & Access owns the entity's shape, GUI Labelling owns the meaning/UI of this one key)
Data
Initial data is seeded from the label text already present in the codebase at the time this feature is deployed: for every UI string a developer has written, a uiLabel record is created with defaultValue = currentValue = that existing text, locale = cs. Going forward, every new UI string introduced by a developer must be accompanied by a corresponding seed record — see Backward Compatibility and Migration below.
Test Data
N/A — not covered in source material; no Test Data location was specified in the source page.
Logging
.info
- label edited (translationKey, locale, previous currentValue, new currentValue, superadmin actor)
.debug
- fallback used when serving a label (translationKey, requested locale, fallback level reached — see "Missing label fallback")
Monitoring
N/A — not covered in source material.
Caching
See Non-Functional Requirements → Caching above.
Backward Compatibility and Migration
There is no EM2 equivalent for the label-editing mechanism itself (superadmin-editable currentValue/defaultValue).
For hint, a partial EM2 source does exist: the dictionary table (entity Dictionary.php) contains 81 keys with prefix field.help.* (36 non-empty), surfaced via the Symfony FormType 'help' option. These are the candidate seed data for hint, but migration is not 1:1 import — EM2 key naming (e.g. field.help.phone) does not match the EM3 translationKey convention, so each key needs an explicit mapping to its EM3 equivalent before import.
Ad hoc EM2 tooltip texts (e.g. *_tooltip dictionary keys, inline v-b-tooltip strings on datatable action buttons) are excluded from migration — they are inconsistent and relate to UI actions, not field explanations.
The field contactTechnicalUserId (the original trigger for this requirement) does not exist in EM2 in any form — its hint will be authored from scratch by a superadmin in EM3, not migrated.
Migration/seeding responsibilities going forward:
- at initial rollout, every existing hardcoded UI string across the already-designed features (Gauge Management, Building Management, etc.) must be inventoried and seeded as a
uiLabelrecord withdefaultValue = currentValue - going forward, introducing a new UI string without a corresponding
uiLabelseed record is a process gap to be caught in code review, not a system-enforced constraint — the "Missing label fallback" behaviour exists specifically to make such gaps visible rather than silently broken
Legal Context
N/A — not addressed in the source Confluence page; migrated as a reference copy without new legal analysis.
Cybersecurity Considerations
N/A — not addressed in the source Confluence page; migrated as a reference copy without new security analysis.
Risk Assessment
N/A — not addressed in the source Confluence page; migrated as a reference copy without new risk analysis.
Auditing, Reporting & Measurement
N/A — not addressed in the source Confluence page; migrated as a reference copy without new analysis. Note: label edits are logged (see Logging above), which is adjacent to auditing but was not framed as such in the source.