Skip to content

Typy, šablony a jazykové verze

Notifikace mají 3 úrovně:

NotificationType   (SKUPINA / kategorie;  unikát: app + code)
        │  1 : N

NotificationTemplate  (POJMENOVANÁ šablona;  unikát: app + type + key)
        │  1 : N

TemplateLocale     (jazyková verze;  unikát: templateId + locale)

Příklad:

Typ "error"  (skupina — drží kanály a předvolby uživatele)
 ├─ Šablona "disk_full"   (key + name + variables)
 │    ├─ cs (subject/body)   ├─ en   └─ de
 └─ Šablona "auth_failed"
      ├─ cs   └─ en
  • Typ = skupina/kategorie. Drží uživatelské předvolby a kanály (emailEnabled, pushEnabled, userCanDisable, defaultEmail, defaultPush). Uživatel se odhlašuje z typu (= ze všech jeho šablon).
  • Šablona = pojmenovaná jednotka obsahu pod typem (key + name + description + variables[]). Variables patří šabloně (společné pro její jazyky).
  • Jazyková verze = subjectTemplate + bodyTemplate pro daný locale.

Jednotka, kterou aplikace odešle a kterou renderujeme = (app, type, template, locale).


1. Notifikační typy (skupina)

Typ definuje kategorii — co se oznamuje, jakými kanály a zda to může uživatel vypnout.

typescript
{
  id: number;
  applicationCode: string;
  code: string;              // unikátní kód v rámci aplikace (např. 'error')
  name: string;              // zobrazovaný název (pro UI preferencí)
  description: string | null;

  emailEnabled: boolean;     // email kanál povolen pro tento typ
  pushEnabled: boolean;      // push kanál povolen pro tento typ
  userCanDisable: boolean;   // může uživatel typ (celou skupinu) vypnout?

  defaultEmail: boolean;     // výchozí stav email pro nové uživatele
  defaultPush: boolean;      // výchozí stav push pro nové uživatele
}

Předvolby a kanály jsou na typu, ne na šabloně. Uživatel se odhlašuje z celé skupiny (typu), ne z jednotlivých šablon.

userCanDisable

Pokud userCanDisable = false, uživatel nemůže typ vypnout ve svých preferencích. Vhodné pro kritické systémové notifikace.

Správa typů

bash
POST   /api/admin/notifications/types/:appCode          # vytvoření
PUT    /api/admin/notifications/types/detail/:id        # aktualizace
DELETE /api/admin/notifications/types/detail/:id        # smazání (kaskádní)

Kaskádní smazání

DELETE /admin/notifications/types/detail/:id smaže typ i všechny jeho šablony i jejich jazykové verze (v transakci). code typu je neměnný (PUT ho needituje), aby se vazba nikdy nerozpadla.


2. Pojmenované šablony (mezivrstva)

Šablona je pojmenovaná jednotka obsahu pod typem.

typescript
{
  id: number;
  type: string;              // kód nadřazeného typu, např. 'error'
  key: string;               // slug unikátní v rámci (app, type), např. 'disk_full'
  name: string;              // zobrazovaný název
  description: string | null;
  variables: TemplateVariable[];  // kontrakt pro `data` (společné pro všechny jazyky)
}
  • key je slug ([a-z0-9_-]), unikátní v rámci (app, type), a je neměnný (rename se nepodporuje — vytvoř novou).
  • variables patří šabloně a jsou společné pro všechny její jazykové verze.

Správa šablon

MetodaCestaÚčel
GET/admin/notifications/types/:appCode/:type/templatesŠablony pod typem (vč. seznamu locale)
POST/admin/notifications/types/:appCode/:type/templatesVytvoř šablonu { key, name, description?, variables? }
PUT/admin/notifications/templates/:idUprav šablonu (name, description, variables)
DELETE/admin/notifications/templates/:idSmaž šablonu i s jazykovými verzemi (kaskáda)

GET vrací:

json
{
  "type": { "id": 3, "code": "error", "name": "Chyby", "...": "..." },
  "templates": [
    { "id": 10, "type": "error", "key": "disk_full", "name": "Disk je plný",
      "description": null, "variables": [ ... ], "locales": ["cs", "en"] }
  ]
}

Validace při zápisu

PodmínkaChyba (4xx, details.code)
type musí existovat jako NotificationType dané aplikaceUNKNOWN_NOTIFICATION_TYPE
key musí být validní slug a unikátní v (app, type)BAD_REQUEST

3. Jazykové verze (subject/body)

Každá šablona má 0..N jazykových verzí — jednu per locale.

typescript
{
  id: number;
  templateId: number;
  locale: string;            // "cs", "en", "de", ...
  subjectTemplate: string;   // Mustache šablona předmětu
  bodyTemplate: string;      // Mustache HTML šablona těla
}

Správa jazykových verzí

MetodaCestaÚčel
GET/admin/notifications/templates/:id/localesJazykové verze šablony
PUT (upsert)/admin/notifications/templates/:id/locales/:localeVytvoř/uprav verzi { subjectTemplate, bodyTemplate }
DELETE/admin/notifications/templates/:id/locales/:localeSmaž jednu jazykovou verzi
  • Upsert podle (templateId, locale) — když verze neexistuje, vytvoří (201); jinak aktualizuje (200). Žádné „already exists".
  • locale musí být povolený jazyk aplikace (Application.supportedLanguages; když aplikace nemá omezení, musí existovat v registru jazyků) → jinak 400 UNSUPPORTED_LOCALE. Normalizuje se na lowercase.

Rozlišení jazyka (fallback řetěz) — v rámci šablony

Při odeslání se vybírá jazyková verze zvolené šablony:

  1. verze v locale z request body (override), jinak v UserProfile.locale (default "cs"),
  2. jinak po řetězu Language.fallback (např. hr → en → cs),
  3. koncový safety-net: libovolná existující jazyková verze té šablony.

Řetěz se spravuje globálně v registru jazyků.


Odeslání: pole type + template

Aplikace cílí šablonu přes type (skupina) a template (key):

jsonc
POST /api/internal/notifications
{
  "recipientEmail": "petr@example.com",
  "appCode": "ui-template",
  "type": "error",          // SKUPINA
  "template": "disk_full",  // KONKRÉTNÍ ŠABLONA
  "locale": "cs",           // volitelné
  "data": { "path": "/var" }
}

Pravidla pro template (zpětná kompatibilita):

StavChování
template vyplněné, existujepoužije se šablona (app, type, template)
template vyplněné, neexistuje400 UNKNOWN_TEMPLATE, details.available = [klíče]
template chybí, typ má 1 šablonupoužije se ta jediná (pokrývá migrovaná data)
template chybí, typ má víc šablon400 TEMPLATE_REQUIRED, details.available = [klíče]
type === "generic"speciál — vestavěná šablona, bez DB

Validace data (per šablona)

required parametry se berou z variables zvolené šablony. Chybí-li → 400 MISSING_TEMPLATE_VARIABLES, details.missing = […]. Systémové (appName, userName, userEmail) doplní backend (neposílat v data).


Parametry šablony (variables)

Šablona deklaruje, jaké parametry vyžaduje. Je to kontrakt, který odesílající aplikace musí splnit v poli data, a zároveň zdroj, ze kterého panel staví nápovědu a náhled.

typescript
{
  key: string;          // povinné, [A-Za-z0-9_.]; používá se jako {{key}}
  label?: string;       // popisek pro admina
  description?: string; // nápověda
  required?: boolean;   // default true
  example?: string;     // ukázková hodnota pro náhled
}

Parametry jsou jazykově nezávislé → patří šabloně a jsou shodné napříč všemi jejími jazykovými verzemi.

Systémové proměnné (appName, userName, userEmail) do variables nepatří — backend je doplňuje automaticky (viz Systémové proměnné).


Registr jazyků

Kurátovaný globální seznam jazyků platformy. Admin jazyky zakládá, pojmenovává a nastavuje jim fallback.

MetodaCestaTělo
GET/admin/languagesLanguage[]
POST/admin/languages{ code, name, fallback? }
PUT/admin/languages/{code}{ name?, fallback? } (code neměnný)
DELETE/admin/languages/{code}

Model: { code: string (lowercase, PK), name: string, fallback: string | null }. fallback = null = kořen. V řetězu nesmí vzniknout cyklus. DELETE se odmítne, pokud je jazyk fallbackem jiného nebo je v Application.supportedLanguages.


Mustache šablony

Šablony používají jednoduchou Mustache syntaxi: vykreslí hodnotu.

Subject:

Nová objednávka č. {{orderNumber}} od {{customerName}}

Body (HTML):

html
<h1 style="color: {{brandPrimaryColor}}">Nová objednávka!</h1>
<p>Přijali jsme objednávku č. <strong>{{orderNumber}}</strong></p>
<p><a href="{{orderUrl}}" style="color: {{brandLinkColor}}">Zobrazit objednávku</a></p>
<hr>
<small>{{brandFooterText}}</small>

Automaticky dostupné proměnné (brand context)

ProměnnáObsah
Hlavní barva brandu (hex)
Tmavší varianta hlavní barvy
Barva pozadí
Barva textu
Barva odkazů
URL loga brandu
Text v patičce
Název brandu
Odesílací email

Brand se načte dle UserProfile.brand příjemce. Pokud brand neexistuje, použije se výchozí brand.

Systémové proměnné

Backend doplňuje do každé šablony tyto proměnné. Odesílající aplikace je nemá posílat v data — jakákoliv hodnota poslaná pod těmito klíči je přepsána:

ProměnnáObsah
Název aplikace (z Application.name)
Jméno příjemce (UserProfile.displayName, fallback na e-mail)
E-mail příjemce

Tyto proměnné se do variables neuvádějí.


Preview šablony

bash
POST /api/admin/notifications/templates/preview
{
  "subjectTemplate": "Objednávka č. {{orderNumber}}",
  "bodyTemplate": "<h1>Přijata!</h1><p>{{orderNumber}} — {{totalAmount}} Kč</p>",
  "data": { "orderNumber": "TEST-001", "totalAmount": "5000" },
  "brand": "atrea"
}

Preview renderuje libovolný subject/body — není vázaný na uloženou šablonu.


Checklist pro nový notifikační typ

  • [ ] Vytvořit NotificationType (POST /admin/notifications/types/:appCode)
  • [ ] Vytvořit pojmenovanou šablonu (POST /admin/notifications/types/:appCode/:type/templates) s key + name + variables
  • [ ] Přidat jazykové verze (PUT /admin/notifications/templates/:id/locales/:locale) — ručně nebo přes Lexicon auto-překlad
  • [ ] Ověřit fallback řetěz v /admin/languages
  • [ ] Otestovat preview s testovacími daty
  • [ ] Ověřit, že aplikace posílá type + template + správná data
  • [ ] Zkontrolovat emailEnabled / pushEnabled a userCanDisable na typu

Auto-překlad přes Lexicon

Šablony lze automaticky dopřeložit přes Lexicon API (DeepL pod kapotou):

  • GET /admin/notifications/lexicon/languages — seznam podporovaných cílových jazyků.
  • POST /admin/notifications/lexicon/translate-template — přeloží subjectTemplate + bodyTemplate. Placeholdery jsou před překladem maskované a po překladu obnovené.

Auto-překlad šablonu neukládá — vrátí přeložený text ke kontrole. Konfigurace: LEXICON_API_URL / LEXICON_API_KEY.

Atrea User API — interní dokumentace