feat(observability): session-replay + in-app feedback on catalog UX failures

The two things only the browser can add on top of the backend catalog-degradation
reporting:

- Replay-on-failure: when the catalog UI renders empty-tree (decoded vehicle, no
  categories) or a drill loadError, capture a browser Sentry warning and flush the
  Session Replay → you can WATCH the user hit the dead-end (serkan's session,
  reproducible). Per-session deduped (one replay/session covers the whole journey).
- In-app feedback: a "Çalışmadı mı? Bildir" button on the empty-parts, empty-tree
  and loadError states opens the Sentry feedback dialog pre-tagged with the
  vehicle/category (+ session replay) — turns a parts shop's complaint into a
  structured, triageable report instead of an email.

Browser events are fingerprinted source="browser" so they form their own
"what users actually saw" issues (carrying replays) next to the server-side
detections. tsc + biome + web build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 19:07:49 +03:00
parent a89eac1820
commit d6c88ede73
7 changed files with 247 additions and 28 deletions

View File

@@ -0,0 +1,63 @@
import { useTranslation } from "@/lib/i18n";
import {
type CatalogIssueContext,
type CatalogIssueKind,
openCatalogFeedback,
reportCatalogDegradation,
} from "@/lib/sentry";
import { Button } from "@sase/ui";
import { Flag } from "lucide-react";
import { useEffect } from "react";
/**
* Fire-and-forget: when `active` becomes true, report the catalog degradation the
* user is looking at to Sentry (with a flushed session replay so it's watchable).
* Deduped per session in the lib, so re-renders are safe.
*/
export function useReportCatalogDegradation(
kind: CatalogIssueKind,
active: boolean,
ctx: CatalogIssueContext,
): void {
const { vehicleId, categoryId, vehicleLabel, categoryName, source, vin } = ctx;
useEffect(() => {
if (!active) return;
void reportCatalogDegradation(kind, {
vehicleId,
categoryId,
vehicleLabel,
categoryName,
source,
vin,
});
}, [active, kind, vehicleId, categoryId, vehicleLabel, categoryName, source, vin]);
}
/**
* "Çalışmadı mı? Bildir" — opens the Sentry feedback dialog pre-tagged with this
* vehicle/category. Shown on empty/error catalog states so a parts shop can
* report a missing/wrong catalog in one click (with the session replay attached)
* instead of emailing.
*/
export function ReportCatalogIssueButton({
ctx,
className,
}: {
ctx: CatalogIssueContext;
className?: string;
}) {
const { t } = useTranslation();
return (
<Button
type="button"
variant="ghost"
size="sm"
className={className}
onClick={() => void openCatalogFeedback(ctx)}
data-faro-user-action-name="report-catalog-issue"
>
<Flag className="mr-1.5 h-4 w-4" />
{t("vehicle.reportIssue")}
</Button>
);
}