# Nahlásit problém (https://docs.vitrinka.ai/cs/qa/report-a-problem)



## Co nahlašující uvidí [#what]

Lidé ve vaší aplikaci uvidí v rohu stránky velké tlačítko **Nahlásit problém** — nebo vůbec nic, pokud widget otevíráte vlastním ovládacím prvkem, třeba položkou v menu Nápověda. Mluví česky nebo anglicky: jazykem prohlížeče, pokud ho vaše aplikace nenastaví.

1. **Nejdřív popis.** Malá karta se zeptá &#x2A;*Co se pokazilo?**. Jedna věta a **Odeslat** je celé hlášení. Karta říká, kolik posledních minut se přikládá, a vypíše, co všechno je přiloženo.
2. **Ukažte nám kde, pokud chcete.** **Ukažte nám kde** zmrazí stránku pod průhlednou vrstvou a nahlašující problém označí kruhem, obdélníkem, šipkou, nebo skrytím, které zakryje soukromou část obrazovky. Značky se číslují 1, 2, 3… a každá může mít poznámku. Dokud neexistuje první značka, drží se vedle ukazatele nápověda, která gesto ukazuje: klikněte, podržte a veďte kolem problému. Z klávesnice Enter značku začne, šipky ji posouvají, Shift se šipkami mění její velikost a Enter ji umístí.
3. **Vlastní klávesová zkratka.** Výchozí žádná není. V nastavení karty (⚙) si ji nahlašující nahraje tím, že ji stiskne — musí obsahovat Ctrl, Alt nebo ⌘, Esc nahrávání zruší — a od té chvíle otevře kartu kdekoli ve vaší aplikaci. Pamatuje si ji ten prohlížeč.
4. **Po odeslání** karta poděkuje a nabídne **Zobrazit hlášení**, pokud vaše instalace provozuje [portál pro nahlašující](/cs/qa/portal). Když se odeslání nepovede, žádné slovo se neztratí; **Zkusit znovu** pokračuje v tomtéž hlášení, nepošle druhé.

Nahlašující, kterého vaše aplikace identifikovala, si může zaškrtnout i **Pošlete mi e-mail, až to bude opravené**.

## Instalace [#install]

Komponenta je hlásicí podoba balíčku `@vitrinka/web`. Každá instalace vitrinky ji servíruje jako jeden soubor, takže stačí script tag:

```html
<script type="module" src="https://vitrinka.example.com/vendor/report-widget-1.mjs"></script>
<vitrinka-report key="vkb_…" locale="cs"></vitrinka-report>
```

Soubor servíruje bez cache ta instalace, která hlášení přijímá (na hostované službě `https://app.vitrinka.ai/vendor/report-widget-1.mjs`), takže se to, co widget zaznamenává, nikdy nerozejde s tím, co server očekává. `base` má výchozí hodnotu origin, ze kterého skript přišel. Soubor je ES modul: při načtení definuje element, pro stránku, která ho chce ovládat skriptem, exportuje `mountReport` a `identify`, a rrweb načítá ze stejné instalace jen tehdy, když projekt stránku nahrává.

V aplikaci s buildem nainstalujte balíček a připojte ho. rrweb, peer závislost, se načte jen tehdy, když projekt stránku nahrává:

```bash
bun add @vitrinka/web rrweb
```

```ts
import { mountReport } from "@vitrinka/web/report";

const report = mountReport({ key: "vkb_…", base: "https://vitrinka.example.com", locale: "cs" });
report.open();
```

V Reactu ho připojte jednou a předávejte mu jazyk aplikace a token nahlašujícího, jak se mění. Přepnutí jazyka kartu překreslí, aniž by se ztratil rozepsaný text nebo značky:

```tsx
import { useEffect, useRef } from "react";
import { mountReport, type ReportHandle } from "@vitrinka/web/report";

export function ReportAProblem({ locale, token }: { locale: string; token?: string }) {
  const report = useRef<ReportHandle | null>(null);
  useEffect(() => {
    report.current = mountReport({ key: "vkb_…", base: "https://vitrinka.example.com" });
    return () => report.current?.unmount();
  }, []);
  useEffect(() => report.current?.setLocale(locale), [locale]);
  useEffect(() => report.current?.identify(token ? { token } : null), [token]);
  return null;
}
```

Stránka běží s jedním záznamem. Je-li připojená pilulka rekordéru z `@vitrinka/web`, záznam patří jí a Nahlásit problém pak posílá hlášení bez něj — na stránku připojte jen jednu podobu, vybranou podle režimu vaší aplikace.

## Volby [#options]

| Volba          | Atribut         | Co dělá                                                                                                                                      |
| -------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `key`          | `key`           | Klíč `vkb_` pro hlášení z daného projektu. Povinný.                                                                                          |
| `base`         | `base`          | Origin vitrinky, kam hlášení odcházejí; u script tagu je výchozí ten, odkud byl skript servírovaný.                                          |
| `button`       | `button`        | `false` skryje plovoucí tlačítko, takže kartu otevírá jen váš vlastní ovládací prvek.                                                        |
| `openSelector` | `open-selector` | CSS selektor vašich vlastních prvků, které kartu otevírají.                                                                                  |
| `locale`       | `locale`        | `cs` nebo `en`. Bez nastavení jazyk prohlížeče, a není-li ani jedním z nich, angličtina. Za běhu ho změníte přes `setLocale()` nebo atribut. |
| `capture`      | —               | Zúží profil záznamu projektu — [níže](#capture).                                                                                             |
| `identity`     | —               | Kdo hlásí, stejně jako `identify()` — [níže](#identity).                                                                                     |

Handle i element nabízejí `open()`, `close()`, `identify()` a `setLocale()`; handle má navíc `unmount()`.

## Klíč pro hlášení [#key]

Klíč vydáte v **Nastavení → Workspace → Tokens** jako klíč pro hlášení z jednoho projektu, spolu s originy, ze kterých smí hlášení přicházet: prázdný seznam nepustí žádný prohlížeč, `*` pustí jakýkoli. Klíč je veřejný. Načte veřejnou konfiguraci projektu a posílá hlášení do toho jednoho projektu; žádné hlášení nikdy nepřečte zpátky. Počet požadavků je omezený pro každý klíč a odesílaná data procházejí [maskováním citlivých údajů](/cs/security/redaction).

## Kdo hlásí [#identity]

Vaše aplikace widgetu řekne, kdo hlásí, jedním ze tří způsobů — a to rozhodne, jestli se hlášení k týmu dostane hned.

| Aplikace předá                 | Co se stane                                                                                                               |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------- |
| Token podepsaný vaším serverem | Nahlašující je ten, koho token uvádí. Hlášení jde rovnou do Inboxu týmu a nahlašující ho sleduje na portálu.              |
| E-mail bez podpisu             | Hlášení se váže k té adrese a čeká na admina. Pro tu adresu není nic čitelné, dokud se její majitel nepřihlásí na portál. |
| Nic                            | Anonymně: hlášení čeká na admina.                                                                                         |

```ts
report.identify({ token });
report.identify({ email, name });
report.identify(null);
```

Identita žije jen v paměti, proto ji předávejte při každém načtení stránky. Funkce `identify` exportovaná z `@vitrinka/web/report` určí nahlašujícího pro všechny widgety na stránce; vlastní `identify()` konkrétního widgetu má přednost.

### Podpisový klíč hostitele [#host-key]

Admin projektu ho vydá v **Nastavení → Projects** na záložce projektu **Report a problem** v části **Host signing key**. Ukáže se jen jednou, začíná `vhk_` a patří mezi serverová tajemství vaší aplikace — nikdy do prohlížeče. Nové vydání ho nahradí a starý klíč okamžitě přestane platit. **revoke** zastaví všechna přihlášení podepsaná hostitelem; dokud nevydáte nový klíč, hlášení z vaší aplikace čekají na kontrolu. Nahrazení i zrušení klíče zároveň odhlásí z portálu každého nahlašujícího, kterého klíč přihlásil — klíč se mění proto, že mohl uniknout. Nahlašující přihlášení kódem nebo odkazem z e-mailu zůstávají přihlášení. Vydání i zrušení se auditují jako `report.host_key` i s počtem ukončených relací portálu.

### Podpis tokenu nahlašujícího [#sign]

Token je kompaktní JWS podepsaný HS256 nad těmito údaji:

| Údaj         | Hodnota                                                                                        |
| ------------ | ---------------------------------------------------------------------------------------------- |
| `sub`        | Identifikátor uživatele ve vaší aplikaci.                                                      |
| `email`      | Jeho adresa, jedna prostá schránka.                                                            |
| `name`       | Nepovinné: jak ho uvidí tým a portál.                                                          |
| `iat`, `exp` | Sekundy od epochy; `exp` nejvýš 24 hodin po `iat`. Vydávejte jeden na každou relaci stránky.   |
| `aud`        | `<workspace>/<project>` — workspace a projekt, ke kterým klíč patří; ukáže se při jeho vydání. |

Tajemstvím pro HMAC je celý klíč včetně `vhk_`. Token, který neprojde ověřením, hlášení nikdy nezahodí: hlášení počká na kontrolu a odpověď na první volání widgetu nese `identityError`, které řekne, co bylo špatně — audience, platnost, podpis jiným klíčem.

V Node:

```ts
import { createHmac } from "node:crypto";

const b64 = (v: string | Buffer) => Buffer.from(v).toString("base64url");

export function reporterToken(user: { id: string; email: string; name?: string }): string {
  const now = Math.floor(Date.now() / 1000);
  const head = b64(JSON.stringify({ alg: "HS256", typ: "JWT" }));
  const body = b64(JSON.stringify({ sub: user.id, email: user.email, name: user.name, iat: now, exp: now + 3600, aud: "acme/shop" }));
  const sig = b64(createHmac("sha256", process.env.VITRINKA_REPORT_HOST_KEY!).update(`${head}.${body}`).digest());
  return `${head}.${body}.${sig}`;
}
```

V Go:

```go
package reporter

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"time"
)

// Token signs a reporter's identity with the project's host signing key.
func Token(key, sub, email, name string) (string, error) {
	now := time.Now().Unix()
	claims, err := json.Marshal(map[string]any{
		"sub": sub, "email": email, "name": name,
		"iat": now, "exp": now + 3600, "aud": "acme/shop",
	})
	if err != nil {
		return "", err
	}
	enc := base64.RawURLEncoding
	body := enc.EncodeToString([]byte(`{"alg":"HS256","typ":"JWT"}`)) + "." + enc.EncodeToString(claims)
	mac := hmac.New(sha256.New, []byte(key))
	mac.Write([]byte(body))
	return body + "." + enc.EncodeToString(mac.Sum(nil)), nil
}
```

Stejný token nahlašujícího přihlásí i na portál — [přihlášení](/cs/qa/portal#signin).

## Profil záznamu [#capture]

Co widget uchovává před nahlášením, určuje profil záznamu projektu. Admin projektu ho nastaví na téže záložce **Report a problem** a widget si ho při načtení přečte se zbytkem konfigurace z `GET /api/v1/report-config`.

| Nastavení | Výchozí                        | Co určuje                                                                                                                                                                                                |
| --------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Úložiště  | úložiště prohlížeče            | `browser` zapisuje maskovaný záznam i do IndexedDB prohlížeče, takže minuty před nahlášením přežijí načítání stránek mezitím; `memory` ho drží jen v otevřené kartě.                                     |
| Okno      | 300 sekund                     | Jak daleko do minulosti záznam sahá: 10 sekund až 30 minut.                                                                                                                                              |
| Velikost  | 12 MiB                         | Strop záznamu, 1 až 64 MiB. Za ním odpadá nejstarší část a záznam se označí jako zkrácený.                                                                                                               |
| Třídy     | interakce, konzole, síť, chyby | `interactions` — maskovaný záznam stránky; `errors` — nezachycené chyby, `console.error` a selhané požadavky; `console` — všechny zprávy konzole; `network` — všechny požadavky; `full` — všechny čtyři. |

Vaše aplikace může profil jen zúžit — paměť místo úložiště prohlížeče, kratší okno, nižší strop, méně tříd — nikdy ho nerozšíří:

```ts
mountReport({ key: "vkb_…", capture: { storage: "memory", windowSec: 120 } });
```

* **Maskuje se před uložením.** Prohlížeč drží přesně to, co by se odeslalo, zamaskované podle profilu maskování projektu, který se nastavuje v hlavičce jeho panelu Reports: `strict` (výchozí) maskuje všechen text i každou hodnotu vstupu, `balanced` hodnoty vstupů, `full` nic. Značka skrytí zakryje svou oblast v každém snímku dřív, než cokoli opustí prohlížeč.
* **Uložený záznam vyprší.** Uložená část z prohlížeče zmizí, když vypadne z okna, když se odešle hlášení z dané karty, a při nejbližším načtení stránky po uplynutí své doby. Přepnutí projektu na paměť ji smaže.
* **Co nahlašující píše, zůstává v paměti** — popis ani poznámky se nikdy neukládají.
* **Z požadavků jen metadata** — metoda, cesta, stav a doba trvání, nikdy hlavička ani tělo.

## Co se stane potom [#next]

* **Založeno hned.** Hlášení, za které vaše aplikace ručí, nebo hlášení napsané na [portálu](/cs/qa/portal), se okamžitě stane úkolem: bugem, který čeká v projektu, nahoře v [Inboxu](/cs/pm/inbox) týmu. Na pozadí se z jeho záznamu stane session s přehráním, na nástěnku chyb projektu přibude karta a každá značka kromě skrytí se stane anotací na obrazovce, kterou nahlašující označil, propojenou s úkolem. Se zapnutou Eve dorazí už roztříděné — [třídění Eve](/cs/pm/inbox#eve).
* **Drženo ke kontrole.** Anonymní hlášení, nebo hlášení s e-mailem, za který nikdo neručí, jde do karantény. Jeho záznam je odložený a dostanou se k němu jen admini workspace, takže nic z něj se nedostane na nástěnku, do vyhledávání ani k AI dřív, než se na něj podívá člověk.

## Karanténa a kontrola [#review]

Admini workspace kontrolují zadržená hlášení v modulu QA pod **Bug reports**.

| Akce                  | Kdo             | Co se stane                                                                                                                                                |
| --------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Prohlédnout           | admin workspace | Co bylo napsáno, značky, konzole, požadavky a přehrání.                                                                                                    |
| Upravit citlivý obsah | admin workspace | Odstranit vybrané záznamy nebo přehrání před uvolněním.                                                                                                    |
| Schválit              | admin workspace | Ze záznamu vznikne session, hlášení přistane na nástěnce chyb projektu a do Inboxu dorazí jako úkol — stejnou cestou jako hlášení, za které aplikace ručí. |
| Zamítnout             | admin workspace | Hlášení se uzavře a nic se neuvolní.                                                                                                                       |

Se zapnutým přepínačem auto-ticket projektu otevře schválení i tiket přes [konektor projektu](/cs/operator/connectors#jira).
