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:
63
apps/web/src/components/catalog/report-catalog-issue.tsx
Normal file
63
apps/web/src/components/catalog/report-catalog-issue.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user