qapture2 0.7.7 → 0.7.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-WU7QMHPK.cjs → chunk-6UNSUNJS.cjs} +311 -287
- package/dist/{chunk-FVQKCQC3.js → chunk-G6O4SIEH.js} +311 -287
- package/dist/index.cjs +5 -5
- package/dist/index.js +1 -1
- package/dist/next.cjs +4 -4
- package/dist/next.js +1 -1
- package/dist/standalone.cjs +2 -2
- package/dist/standalone.js +1 -1
- package/package.json +3 -2
|
@@ -1734,6 +1734,8 @@ var STR = {
|
|
|
1734
1734
|
loc_show: "Show captured location",
|
|
1735
1735
|
loc_hide: "Hide location",
|
|
1736
1736
|
loc_locate: "Locate on page",
|
|
1737
|
+
loc_other_page: "This one was filed on {path}, not the page you are on.",
|
|
1738
|
+
loc_go_there: "Go to {path}",
|
|
1737
1739
|
journey_title: "Testing journey",
|
|
1738
1740
|
journey_generic: "No test plan was set up for this project, so this is a general one that fits any app. Work through it, or ask whoever set up Qapture to add the real journey to qa.config.",
|
|
1739
1741
|
export_name_title: "Name your export",
|
|
@@ -1862,6 +1864,13 @@ var STR = {
|
|
|
1862
1864
|
retest_not_found: "Can't find that spot on this page \u2014 open the right page first",
|
|
1863
1865
|
before_label: "Before",
|
|
1864
1866
|
after_label: "After",
|
|
1867
|
+
followup_label: "What happened this time",
|
|
1868
|
+
followup_add: "Say what happened",
|
|
1869
|
+
followup_placeholder: "What did it do this time, and how is that different from what you asked for?",
|
|
1870
|
+
followup_hint: "The first note stays as it is. This is added under it as round two.",
|
|
1871
|
+
keep_only_retest: "Keep only these {n}",
|
|
1872
|
+
keep_only_retest_q: "Delete the other {n}?",
|
|
1873
|
+
keep_only_retest_hint: "Delete every note that is not waiting to be re-tested",
|
|
1865
1874
|
welcome_title: "Testing {brand}",
|
|
1866
1875
|
welcome_capture: 'Click "Capture from page", then click whatever looks wrong.',
|
|
1867
1876
|
welcome_say: "Say what you expected in your own words. A screenshot is taken for you.",
|
|
@@ -1882,6 +1891,7 @@ var STR = {
|
|
|
1882
1891
|
capture_screen: "Whole screen",
|
|
1883
1892
|
walk_title: "Walkthrough",
|
|
1884
1893
|
walk_take_me: "Take me to {path}",
|
|
1894
|
+
walk_taking_you: "Taking you there\u2026",
|
|
1885
1895
|
walk_reload_hint: "Reload the page instead (use this if the app didn't move)",
|
|
1886
1896
|
walk_note_title: "Point {n}",
|
|
1887
1897
|
walk_still_broken: "Still broken",
|
|
@@ -1932,6 +1942,8 @@ var STR = {
|
|
|
1932
1942
|
loc_show: "\u0625\u0638\u0647\u0627\u0631 \u0627\u0644\u0645\u0648\u0642\u0639 \u0627\u0644\u0645\u064F\u0644\u062A\u0642\u064E\u0637",
|
|
1933
1943
|
loc_hide: "\u0625\u062E\u0641\u0627\u0621 \u0627\u0644\u0645\u0648\u0642\u0639",
|
|
1934
1944
|
loc_locate: "\u0625\u0638\u0647\u0627\u0631 \u0639\u0644\u0649 \u0627\u0644\u0635\u0641\u062D\u0629",
|
|
1945
|
+
loc_other_page: "\u0633\u064F\u062C\u0651\u0644\u062A \u0647\u0630\u0647 \u0641\u064A {path}\u060C \u0648\u0644\u064A\u0633\u062A \u0627\u0644\u0635\u0641\u062D\u0629 \u0627\u0644\u062A\u064A \u0623\u0646\u062A \u0641\u064A\u0647\u0627.",
|
|
1946
|
+
loc_go_there: "\u0627\u0630\u0647\u0628 \u0625\u0644\u0649 {path}",
|
|
1935
1947
|
journey_title: "\u0631\u062D\u0644\u0629 \u0627\u0644\u0627\u062E\u062A\u0628\u0627\u0631",
|
|
1936
1948
|
journey_generic: "\u0644\u0645 \u064A\u062A\u0645 \u0625\u0639\u062F\u0627\u062F \u062E\u0637\u0629 \u0627\u062E\u062A\u0628\u0627\u0631 \u0644\u0647\u0630\u0627 \u0627\u0644\u0645\u0634\u0631\u0648\u0639\u060C \u0644\u0630\u0644\u0643 \u0647\u0630\u0647 \u062E\u0637\u0629 \u0639\u0627\u0645\u0629 \u062A\u0635\u0644\u062D \u0644\u0623\u064A \u062A\u0637\u0628\u064A\u0642. \u0627\u0639\u0645\u0644 \u0628\u0647\u0627\u060C \u0623\u0648 \u0627\u0637\u0644\u0628 \u0645\u0645\u0646 \u062B\u0628\u0651\u062A Qapture \u0623\u0646 \u064A\u0636\u064A\u0641 \u0631\u062D\u0644\u0629 \u0627\u0644\u0645\u0634\u0631\u0648\u0639 \u0641\u064A qa.config.",
|
|
1937
1949
|
export_name_title: "\u0633\u0645\u0650\u0651 \u0645\u0644\u0641 \u0627\u0644\u062A\u0635\u062F\u064A\u0631",
|
|
@@ -2060,6 +2072,13 @@ var STR = {
|
|
|
2060
2072
|
retest_not_found: "\u062A\u0639\u0630\u0651\u0631 \u0625\u064A\u062C\u0627\u062F \u0630\u0644\u0643 \u0627\u0644\u0645\u0648\u0636\u0639 \u0641\u064A \u0647\u0630\u0647 \u0627\u0644\u0635\u0641\u062D\u0629 \u2014 \u0627\u0641\u062A\u062D \u0627\u0644\u0635\u0641\u062D\u0629 \u0627\u0644\u0635\u062D\u064A\u062D\u0629 \u0623\u0648\u0644\u064B\u0627",
|
|
2061
2073
|
before_label: "\u0642\u0628\u0644",
|
|
2062
2074
|
after_label: "\u0628\u0639\u062F",
|
|
2075
|
+
followup_label: "\u0645\u0627\u0630\u0627 \u062D\u062F\u062B \u0647\u0630\u0647 \u0627\u0644\u0645\u0631\u0629",
|
|
2076
|
+
followup_add: "\u0627\u0643\u062A\u0628 \u0645\u0627\u0630\u0627 \u062D\u062F\u062B",
|
|
2077
|
+
followup_placeholder: "\u0645\u0627\u0630\u0627 \u0641\u0639\u0644 \u0647\u0630\u0647 \u0627\u0644\u0645\u0631\u0629\u060C \u0648\u0643\u064A\u0641 \u064A\u062E\u062A\u0644\u0641 \u0630\u0644\u0643 \u0639\u0645\u0651\u0627 \u0637\u0644\u0628\u062A\u0647\u061F",
|
|
2078
|
+
followup_hint: "\u0627\u0644\u0645\u0644\u0627\u062D\u0638\u0629 \u0627\u0644\u0623\u0648\u0644\u0649 \u062A\u0628\u0642\u0649 \u0643\u0645\u0627 \u0647\u064A. \u0647\u0630\u0647 \u062A\u064F\u0636\u0627\u0641 \u062A\u062D\u062A\u0647\u0627 \u0643\u062C\u0648\u0644\u0629 \u062B\u0627\u0646\u064A\u0629.",
|
|
2079
|
+
keep_only_retest: "\u0623\u0628\u0642\u0650 \u0647\u0630\u0647 \u0627\u0644\u0640 {n} \u0641\u0642\u0637",
|
|
2080
|
+
keep_only_retest_q: "\u062D\u0630\u0641 \u0627\u0644\u0640 {n} \u0627\u0644\u0623\u062E\u0631\u0649\u061F",
|
|
2081
|
+
keep_only_retest_hint: "\u062D\u0630\u0641 \u0643\u0644 \u0645\u0644\u0627\u062D\u0638\u0629 \u0644\u064A\u0633\u062A \u0628\u0627\u0646\u062A\u0638\u0627\u0631 \u0625\u0639\u0627\u062F\u0629 \u0627\u0644\u0627\u062E\u062A\u0628\u0627\u0631",
|
|
2063
2082
|
welcome_title: "\u0627\u062E\u062A\u0628\u0627\u0631 {brand}",
|
|
2064
2083
|
welcome_capture: "\u0627\u0636\u063A\u0637 \xAB\u0627\u0644\u062A\u0642\u0627\u0637 \u0645\u0646 \u0627\u0644\u0635\u0641\u062D\u0629\xBB \u062B\u0645 \u0627\u0636\u063A\u0637 \u0639\u0644\u0649 \u0645\u0627 \u064A\u0628\u062F\u0648 \u062E\u0627\u0637\u0626\u064B\u0627.",
|
|
2065
2084
|
welcome_say: "\u0627\u0643\u062A\u0628 \u0645\u0627 \u0643\u0646\u062A \u062A\u062A\u0648\u0642\u0639\u0647 \u0628\u0643\u0644\u0645\u0627\u062A\u0643. \u062A\u064F\u0644\u062A\u0642\u0637 \u0627\u0644\u0635\u0648\u0631\u0629 \u062A\u0644\u0642\u0627\u0626\u064A\u064B\u0627.",
|
|
@@ -2080,6 +2099,7 @@ var STR = {
|
|
|
2080
2099
|
capture_screen: "\u0627\u0644\u0634\u0627\u0634\u0629 \u0643\u0627\u0645\u0644\u0629",
|
|
2081
2100
|
walk_title: "\u0627\u0644\u062C\u0648\u0644\u0629",
|
|
2082
2101
|
walk_take_me: "\u062E\u0630\u0646\u064A \u0625\u0644\u0649 {path}",
|
|
2102
|
+
walk_taking_you: "\u062C\u0627\u0631\u064D \u0646\u0642\u0644\u0643\u2026",
|
|
2083
2103
|
walk_reload_hint: "\u0623\u0639\u062F \u062A\u062D\u0645\u064A\u0644 \u0627\u0644\u0635\u0641\u062D\u0629 \u0628\u062F\u0644\u064B\u0627 \u0645\u0646 \u0630\u0644\u0643 (\u0627\u0633\u062A\u062E\u062F\u0645\u0647 \u0625\u0630\u0627 \u0644\u0645 \u064A\u0646\u062A\u0642\u0644 \u0627\u0644\u062A\u0637\u0628\u064A\u0642)",
|
|
2084
2104
|
walk_note_title: "\u0627\u0644\u0646\u0642\u0637\u0629 {n}",
|
|
2085
2105
|
walk_still_broken: "\u0645\u0627 \u0632\u0627\u0644 \u0645\u0639\u0637\u0644\u064B\u0627",
|
|
@@ -2104,18 +2124,6 @@ function pick(value, lang) {
|
|
|
2104
2124
|
if (typeof value === "string") return value;
|
|
2105
2125
|
return value[lang] ?? value.en ?? "";
|
|
2106
2126
|
}
|
|
2107
|
-
|
|
2108
|
-
// src/lib/coverage.ts
|
|
2109
|
-
var RISK_COLORS = {
|
|
2110
|
-
red: "#FF6B6B",
|
|
2111
|
-
// --qa-danger
|
|
2112
|
-
amber: "#FBBF24",
|
|
2113
|
-
// --qa-warn
|
|
2114
|
-
green: "#34D399",
|
|
2115
|
-
// --qa-success
|
|
2116
|
-
none: "#5B616B"
|
|
2117
|
-
// --qa-neutral
|
|
2118
|
-
};
|
|
2119
2127
|
function computeCoverage(journey, guideChecked, skipped) {
|
|
2120
2128
|
const red = { total: 0, covered: 0 };
|
|
2121
2129
|
const amber = { total: 0, covered: 0 };
|
|
@@ -2969,16 +2977,19 @@ function clipToPaintedArea(el, rect) {
|
|
|
2969
2977
|
let top = rect.top;
|
|
2970
2978
|
let right = rect.left + rect.width;
|
|
2971
2979
|
let bottom = rect.top + rect.height;
|
|
2980
|
+
const doc = el.ownerDocument;
|
|
2981
|
+
const root2 = doc ? doc.documentElement : null;
|
|
2982
|
+
const body = doc ? doc.body : null;
|
|
2983
|
+
const rootStyle = root2 ? safeComputedStyle(root2) : null;
|
|
2984
|
+
const bodyStyle = body ? safeComputedStyle(body) : null;
|
|
2972
2985
|
let node = el.parentElement;
|
|
2973
|
-
while (node) {
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
const clipsX = style.overflowX !== "visible";
|
|
2981
|
-
const clipsY = style.overflowY !== "visible";
|
|
2986
|
+
while (node && node !== root2) {
|
|
2987
|
+
const style = safeComputedStyle(node);
|
|
2988
|
+
if (!style) break;
|
|
2989
|
+
const propagatedX = node === body && rootStyle?.overflowX === "visible";
|
|
2990
|
+
const propagatedY = node === body && rootStyle?.overflowY === "visible";
|
|
2991
|
+
const clipsX = !propagatedX && style.overflowX !== "visible";
|
|
2992
|
+
const clipsY = !propagatedY && style.overflowY !== "visible";
|
|
2982
2993
|
if (clipsX || clipsY) {
|
|
2983
2994
|
const b = node.getBoundingClientRect();
|
|
2984
2995
|
const padLeft = b.left + node.clientLeft;
|
|
@@ -2994,9 +3005,33 @@ function clipToPaintedArea(el, rect) {
|
|
|
2994
3005
|
}
|
|
2995
3006
|
node = node.parentElement;
|
|
2996
3007
|
}
|
|
3008
|
+
const vpX = usedViewportOverflow(rootStyle?.overflowX, bodyStyle?.overflowX);
|
|
3009
|
+
const vpY = usedViewportOverflow(rootStyle?.overflowY, bodyStyle?.overflowY);
|
|
3010
|
+
if (vpX === "hidden" || vpX === "clip" || vpY === "hidden" || vpY === "clip") {
|
|
3011
|
+
const { vw, vh } = viewportSize();
|
|
3012
|
+
if (vpX === "hidden" || vpX === "clip") {
|
|
3013
|
+
left = Math.max(left, 0);
|
|
3014
|
+
right = Math.min(right, vw);
|
|
3015
|
+
}
|
|
3016
|
+
if (vpY === "hidden" || vpY === "clip") {
|
|
3017
|
+
top = Math.max(top, 0);
|
|
3018
|
+
bottom = Math.min(bottom, vh);
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
2997
3021
|
if (right - left < 2 || bottom - top < 2) return rect;
|
|
2998
3022
|
return { left, top, width: right - left, height: bottom - top };
|
|
2999
3023
|
}
|
|
3024
|
+
function safeComputedStyle(node) {
|
|
3025
|
+
try {
|
|
3026
|
+
return getComputedStyle(node);
|
|
3027
|
+
} catch {
|
|
3028
|
+
return null;
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
function usedViewportOverflow(rootValue, bodyValue) {
|
|
3032
|
+
if (rootValue && rootValue !== "visible") return rootValue;
|
|
3033
|
+
return bodyValue || "visible";
|
|
3034
|
+
}
|
|
3000
3035
|
function intersectViewport(rect) {
|
|
3001
3036
|
const { vw, vh } = viewportSize();
|
|
3002
3037
|
const left = Math.max(0, rect.left);
|
|
@@ -3191,6 +3226,16 @@ function noteToMarkdown(note, opts) {
|
|
|
3191
3226
|
}
|
|
3192
3227
|
lines.push("");
|
|
3193
3228
|
lines.push(oneLine(note.description) ? note.description.trim() : "_(no description)_");
|
|
3229
|
+
if (note.followUp && oneLine(note.followUp)) {
|
|
3230
|
+
lines.push("");
|
|
3231
|
+
lines.push(
|
|
3232
|
+
"> **This one came back \u2014 round 2.** The point above was already worked on once and it is still not right. What follows is the tester re-testing it, so treat it as the current ask and the point above as the history of what was originally wanted."
|
|
3233
|
+
);
|
|
3234
|
+
lines.push("");
|
|
3235
|
+
lines.push(`**What happened this time**${note.followUpAt ? ` (${oneLine(note.followUpAt)})` : ""}`);
|
|
3236
|
+
lines.push("");
|
|
3237
|
+
lines.push(note.followUp.trim());
|
|
3238
|
+
}
|
|
3194
3239
|
const recordedSteps = note.context?.steps ?? [];
|
|
3195
3240
|
if (recordedSteps.length) {
|
|
3196
3241
|
const t0 = Date.parse(note.timestamp) || recordedSteps[recordedSteps.length - 1].t;
|
|
@@ -3277,7 +3322,7 @@ function fmtPct(n, d) {
|
|
|
3277
3322
|
}
|
|
3278
3323
|
function summariseSession(notes, stamp) {
|
|
3279
3324
|
if (!notes.length) return "No points captured.";
|
|
3280
|
-
let bugs = 0, enhancements = 0, designs = 0, verified = 0, awaiting = 0;
|
|
3325
|
+
let bugs = 0, enhancements = 0, designs = 0, verified = 0, awaiting = 0, secondRound = 0;
|
|
3281
3326
|
const pages = /* @__PURE__ */ new Set();
|
|
3282
3327
|
let earliest = Number.POSITIVE_INFINITY;
|
|
3283
3328
|
let latest = 0;
|
|
@@ -3289,6 +3334,7 @@ function summariseSession(notes, stamp) {
|
|
|
3289
3334
|
const status = n.status ?? "open";
|
|
3290
3335
|
if (status === "verified") verified++;
|
|
3291
3336
|
else if (status === "fixed") awaiting++;
|
|
3337
|
+
if (n.followUp && n.followUp.trim()) secondRound++;
|
|
3292
3338
|
pages.add((n.route || "/").split("?")[0]);
|
|
3293
3339
|
const t = Date.parse(n.timestamp);
|
|
3294
3340
|
if (Number.isFinite(t)) {
|
|
@@ -3304,6 +3350,7 @@ function summariseSession(notes, stamp) {
|
|
|
3304
3350
|
if (designs) parts.push(`${designs} design`);
|
|
3305
3351
|
if (verified) parts.push(`${verified} verified`);
|
|
3306
3352
|
if (awaiting) parts.push(`${awaiting} awaiting re-test`);
|
|
3353
|
+
if (secondRound) parts.push(`${secondRound} back for a second round`);
|
|
3307
3354
|
parts.push(`${pages.size} page${pages.size === 1 ? "" : "s"}`);
|
|
3308
3355
|
if (Number.isFinite(earliest) && latest > earliest) {
|
|
3309
3356
|
const minutes = Math.max(1, Math.round((latest - earliest) / 6e4));
|
|
@@ -3358,6 +3405,14 @@ The tester wants something added or changed: a new section, a new feature, a dif
|
|
|
3358
3405
|
|
|
3359
3406
|
Push back only when you have a real reason: it would break something, or it conflicts with an invariant below. Then say so plainly and propose the alternative you would build instead.
|
|
3360
3407
|
|
|
3408
|
+
### A point with a "round 2" on it
|
|
3409
|
+
|
|
3410
|
+
Some points carry a **round 2** block: the same finding, already worked on once, re-tested, and still not right. Two things follow from that.
|
|
3411
|
+
|
|
3412
|
+
First, **the round-2 text is the current ask** and the paragraph above it is history \u2014 read the history to understand what was wanted, act on the round 2.
|
|
3413
|
+
|
|
3414
|
+
Second, **the previous attempt is evidence.** Something about the first reading was wrong, so do not simply do it again more carefully. Work out what was misunderstood the first time and say so in one sentence, then fix that. If a "Before"/"After" screenshot pair is attached, the After is what the tester was looking at when they wrote the round 2.
|
|
3415
|
+
|
|
3361
3416
|
### When the tag and the words disagree
|
|
3362
3417
|
|
|
3363
3418
|
The tag sets your starting posture; **the words win.** A point tagged Design that says "this crashes" is a bug \u2014 treat it as one, and mention that you changed lane. People pick the wrong chip all the time; that is not a reason to do the wrong work.
|
|
@@ -4162,6 +4217,13 @@ var DENSE_NOTES_KEY = "denseNotes";
|
|
|
4162
4217
|
var PANEL_OPEN_KEY = "panelOpen";
|
|
4163
4218
|
var ACTIVE_TAB_KEY = "activeTab";
|
|
4164
4219
|
var WALK_KEY = "walk";
|
|
4220
|
+
var SOFT_NAV_PROOF_MS = 700;
|
|
4221
|
+
function pageSignature() {
|
|
4222
|
+
if (typeof document === "undefined" || !document.body) return "";
|
|
4223
|
+
const body = document.body;
|
|
4224
|
+
const text = body.textContent || "";
|
|
4225
|
+
return [document.title, body.childElementCount, text.length, text.slice(0, 200)].join("\0");
|
|
4226
|
+
}
|
|
4165
4227
|
var GUIDE_SKIPPED_KEY = "guideSkipped";
|
|
4166
4228
|
var NOTICE_QUEUE_CAP = 3;
|
|
4167
4229
|
var NOTICE_DURATION_INFO = 4e3;
|
|
@@ -4602,6 +4664,11 @@ function QaProvider({
|
|
|
4602
4664
|
}
|
|
4603
4665
|
if (patch.severity !== void 0) updated.severity = patch.severity;
|
|
4604
4666
|
if (patch.status !== void 0) updated.status = patch.status;
|
|
4667
|
+
if (patch.followUp !== void 0) {
|
|
4668
|
+
const text = patch.followUp.trim();
|
|
4669
|
+
updated.followUp = text || void 0;
|
|
4670
|
+
updated.followUpAt = text ? nowIso() : void 0;
|
|
4671
|
+
}
|
|
4605
4672
|
applyNotes((prev) => prev.map((n) => n.id === id ? updated : n));
|
|
4606
4673
|
const persisted = await idb.put(updated);
|
|
4607
4674
|
if (!persisted) {
|
|
@@ -5193,16 +5260,23 @@ function QaProvider({
|
|
|
5193
5260
|
const walkNavigate = useCallback((path, hard = false) => {
|
|
5194
5261
|
if (typeof window === "undefined" || !path) return;
|
|
5195
5262
|
const target = path.startsWith("/") ? path : `/${path}`;
|
|
5196
|
-
if (hard) {
|
|
5263
|
+
if (hard || target === window.location.pathname) {
|
|
5197
5264
|
window.location.assign(target);
|
|
5198
5265
|
return;
|
|
5199
5266
|
}
|
|
5267
|
+
const before = pageSignature();
|
|
5200
5268
|
try {
|
|
5201
|
-
window.history.pushState(
|
|
5202
|
-
window.dispatchEvent(new PopStateEvent("popstate"));
|
|
5269
|
+
window.history.pushState(window.history.state, "", target);
|
|
5270
|
+
window.dispatchEvent(new PopStateEvent("popstate", { state: window.history.state }));
|
|
5203
5271
|
} catch {
|
|
5204
5272
|
window.location.assign(target);
|
|
5273
|
+
return;
|
|
5205
5274
|
}
|
|
5275
|
+
window.setTimeout(() => {
|
|
5276
|
+
if (window.location.pathname !== target) return;
|
|
5277
|
+
if (pageSignature() !== before) return;
|
|
5278
|
+
window.location.assign(target);
|
|
5279
|
+
}, SOFT_NAV_PROOF_MS);
|
|
5206
5280
|
}, []);
|
|
5207
5281
|
useEffect(() => {
|
|
5208
5282
|
if (typeof window === "undefined") return;
|
|
@@ -6085,11 +6159,17 @@ function flashLocate(target) {
|
|
|
6085
6159
|
paint(rect);
|
|
6086
6160
|
}
|
|
6087
6161
|
}
|
|
6088
|
-
function LocationReveal({
|
|
6089
|
-
|
|
6162
|
+
function LocationReveal({
|
|
6163
|
+
target,
|
|
6164
|
+
route
|
|
6165
|
+
}) {
|
|
6166
|
+
const { t, walkNavigate } = useQa();
|
|
6090
6167
|
const [open, setOpen] = useState(false);
|
|
6091
6168
|
if (!target) return null;
|
|
6092
6169
|
const r = target.rect;
|
|
6170
|
+
const notePath = (route || "").split("?")[0].split("#")[0];
|
|
6171
|
+
const here = typeof window === "undefined" ? "" : window.location.pathname;
|
|
6172
|
+
const elsewhere = !!notePath && notePath !== here;
|
|
6093
6173
|
return /* @__PURE__ */ jsxs("div", { className: "qa-rounded-lg qa-border qa-border-subtle qa-bg-2", children: [
|
|
6094
6174
|
/* @__PURE__ */ jsxs("div", { className: "qa-flex qa-items-center qa-gap-1.5 qa-px-2 qa-py-1.5 qa-text-11", children: [
|
|
6095
6175
|
/* @__PURE__ */ jsx(Icon, { name: "CheckCircle2", size: 14, className: "qa-text-success" }),
|
|
@@ -6153,7 +6233,21 @@ function LocationReveal({ target }) {
|
|
|
6153
6233
|
"\xD7",
|
|
6154
6234
|
Math.round(r.height)
|
|
6155
6235
|
] }),
|
|
6156
|
-
/* @__PURE__ */ jsxs(
|
|
6236
|
+
elsewhere ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6237
|
+
/* @__PURE__ */ jsx("p", { className: "qa-mt-1 qa-mb-1 qa-m-0 qa-text-lo", children: t("loc_other_page", { path: notePath }) }),
|
|
6238
|
+
/* @__PURE__ */ jsxs(
|
|
6239
|
+
"button",
|
|
6240
|
+
{
|
|
6241
|
+
onClick: () => walkNavigate(notePath),
|
|
6242
|
+
className: "qa-inline-flex qa-items-center qa-gap-1 qa-rounded-md qa-px-2 qa-py-1 qa-font-medium qa-tap qa-bg-accent",
|
|
6243
|
+
style: { border: "none", cursor: "pointer" },
|
|
6244
|
+
children: [
|
|
6245
|
+
/* @__PURE__ */ jsx(Icon, { name: "MapPinned", size: 12 }),
|
|
6246
|
+
t("loc_go_there", { path: notePath })
|
|
6247
|
+
]
|
|
6248
|
+
}
|
|
6249
|
+
)
|
|
6250
|
+
] }) : /* @__PURE__ */ jsxs(
|
|
6157
6251
|
"button",
|
|
6158
6252
|
{
|
|
6159
6253
|
onClick: () => flashLocate(target),
|
|
@@ -6597,6 +6691,8 @@ function NoteItem({
|
|
|
6597
6691
|
const thumbUrl = useObjectUrl(editing ? img ?? void 0 : note.screenshot);
|
|
6598
6692
|
const afterUrl = useObjectUrl(note.afterScreenshot);
|
|
6599
6693
|
const [retesting, setRetesting] = useState(false);
|
|
6694
|
+
const [editingFollowUp, setEditingFollowUp] = useState(false);
|
|
6695
|
+
const [followUpDraft, setFollowUpDraft] = useState(note.followUp ?? "");
|
|
6600
6696
|
const [open, setOpen] = useState(false);
|
|
6601
6697
|
const severity = note.severity ?? "bug";
|
|
6602
6698
|
const status = note.status ?? "open";
|
|
@@ -6869,7 +6965,7 @@ function NoteItem({
|
|
|
6869
6965
|
/* @__PURE__ */ jsx(Icon, { name: "MapPin", size: 12, className: "qa-shrink-0" }),
|
|
6870
6966
|
/* @__PURE__ */ jsx("span", { className: "qa-truncate qa-dir-ltr", title: note.url, children: note.route })
|
|
6871
6967
|
] }),
|
|
6872
|
-
note.target && /* @__PURE__ */ jsx(LocationReveal, { target: note.target }),
|
|
6968
|
+
note.target && /* @__PURE__ */ jsx(LocationReveal, { target: note.target, route: note.route }),
|
|
6873
6969
|
contextEventCount > 0 && /* @__PURE__ */ jsx("div", { children: t("context_attached", { n: contextEventCount }) }),
|
|
6874
6970
|
stepCount > 0 && /* @__PURE__ */ jsxs("div", { className: "qa-flex qa-items-center qa-gap-1", children: [
|
|
6875
6971
|
/* @__PURE__ */ jsx(Icon, { name: "ClipboardList", size: 12, className: "qa-shrink-0" }),
|
|
@@ -6930,7 +7026,96 @@ function NoteItem({
|
|
|
6930
7026
|
style: { borderColor: "var(--qa-success)" }
|
|
6931
7027
|
}
|
|
6932
7028
|
)
|
|
6933
|
-
] })
|
|
7029
|
+
] }),
|
|
7030
|
+
(status === "fixed" || note.followUp) && /* @__PURE__ */ jsx(
|
|
7031
|
+
"div",
|
|
7032
|
+
{
|
|
7033
|
+
className: "qa-mt-2 qa-rounded-lg qa-border qa-border-subtle qa-bg-2 qa-p-2",
|
|
7034
|
+
"data-qa-followup": "true",
|
|
7035
|
+
children: editingFollowUp ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7036
|
+
/* @__PURE__ */ jsx("label", { className: "qa-text-10 qa-text-lo", htmlFor: `qa-fu-${note.id}`, children: t("followup_label") }),
|
|
7037
|
+
/* @__PURE__ */ jsx(
|
|
7038
|
+
"textarea",
|
|
7039
|
+
{
|
|
7040
|
+
id: `qa-fu-${note.id}`,
|
|
7041
|
+
value: followUpDraft,
|
|
7042
|
+
onChange: (e) => setFollowUpDraft(e.target.value),
|
|
7043
|
+
placeholder: t("followup_placeholder"),
|
|
7044
|
+
rows: 3,
|
|
7045
|
+
className: "qa-mt-1 qa-w-full qa-rounded-lg qa-border qa-border-subtle qa-bg-1 qa-p-2 qa-text-xs qa-text-hi",
|
|
7046
|
+
style: { outline: "none", resize: "vertical" }
|
|
7047
|
+
}
|
|
7048
|
+
),
|
|
7049
|
+
/* @__PURE__ */ jsx("p", { className: "qa-mt-1 qa-text-10 qa-text-lo", children: t("followup_hint") }),
|
|
7050
|
+
/* @__PURE__ */ jsxs("div", { className: "qa-mt-1.5 qa-flex qa-gap-2", children: [
|
|
7051
|
+
/* @__PURE__ */ jsxs(
|
|
7052
|
+
"button",
|
|
7053
|
+
{
|
|
7054
|
+
type: "button",
|
|
7055
|
+
onClick: () => {
|
|
7056
|
+
void updateNote(note.id, { followUp: followUpDraft });
|
|
7057
|
+
setEditingFollowUp(false);
|
|
7058
|
+
},
|
|
7059
|
+
className: "qa-tap qa-inline-flex qa-items-center qa-gap-1 qa-rounded-lg qa-bg-accent qa-px-2 qa-py-1 qa-text-11 qa-font-semibold",
|
|
7060
|
+
style: { border: "none", cursor: "pointer" },
|
|
7061
|
+
children: [
|
|
7062
|
+
/* @__PURE__ */ jsx(Icon, { name: "Check", size: 12 }),
|
|
7063
|
+
t("save")
|
|
7064
|
+
]
|
|
7065
|
+
}
|
|
7066
|
+
),
|
|
7067
|
+
/* @__PURE__ */ jsx(
|
|
7068
|
+
"button",
|
|
7069
|
+
{
|
|
7070
|
+
type: "button",
|
|
7071
|
+
onClick: () => {
|
|
7072
|
+
setFollowUpDraft(note.followUp ?? "");
|
|
7073
|
+
setEditingFollowUp(false);
|
|
7074
|
+
},
|
|
7075
|
+
className: "qa-tap qa-rounded-lg qa-border qa-border-subtle qa-px-2 qa-py-1 qa-text-11 qa-text-mid",
|
|
7076
|
+
style: { background: "transparent", cursor: "pointer" },
|
|
7077
|
+
children: t("cancel")
|
|
7078
|
+
}
|
|
7079
|
+
)
|
|
7080
|
+
] })
|
|
7081
|
+
] }) : note.followUp ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7082
|
+
/* @__PURE__ */ jsxs("div", { className: "qa-flex qa-items-center qa-gap-1", children: [
|
|
7083
|
+
/* @__PURE__ */ jsx(Icon, { name: "RotateCcw", size: 11, className: "qa-shrink-0 qa-text-warn" }),
|
|
7084
|
+
/* @__PURE__ */ jsx("span", { className: "qa-text-10 qa-text-warn", children: t("followup_label") }),
|
|
7085
|
+
/* @__PURE__ */ jsx(
|
|
7086
|
+
"button",
|
|
7087
|
+
{
|
|
7088
|
+
type: "button",
|
|
7089
|
+
onClick: () => {
|
|
7090
|
+
setFollowUpDraft(note.followUp ?? "");
|
|
7091
|
+
setEditingFollowUp(true);
|
|
7092
|
+
},
|
|
7093
|
+
"aria-label": t("edit"),
|
|
7094
|
+
className: "qa-tap-icon qa-ms-auto qa-text-lo",
|
|
7095
|
+
style: { background: "transparent", border: "none", cursor: "pointer" },
|
|
7096
|
+
children: /* @__PURE__ */ jsx(Icon, { name: "Pencil", size: 11 })
|
|
7097
|
+
}
|
|
7098
|
+
)
|
|
7099
|
+
] }),
|
|
7100
|
+
/* @__PURE__ */ jsx("p", { className: "qa-mt-1 qa-whitespace-pre-wrap qa-break-words qa-text-xs qa-text-hi", children: note.followUp })
|
|
7101
|
+
] }) : /* @__PURE__ */ jsxs(
|
|
7102
|
+
"button",
|
|
7103
|
+
{
|
|
7104
|
+
type: "button",
|
|
7105
|
+
onClick: () => {
|
|
7106
|
+
setFollowUpDraft("");
|
|
7107
|
+
setEditingFollowUp(true);
|
|
7108
|
+
},
|
|
7109
|
+
className: "qa-tap qa-inline-flex qa-items-center qa-gap-1 qa-text-11 qa-font-medium qa-text-accent",
|
|
7110
|
+
style: { background: "transparent", border: "none", cursor: "pointer" },
|
|
7111
|
+
children: [
|
|
7112
|
+
/* @__PURE__ */ jsx(Icon, { name: "Plus", size: 12 }),
|
|
7113
|
+
t("followup_add")
|
|
7114
|
+
]
|
|
7115
|
+
}
|
|
7116
|
+
)
|
|
7117
|
+
}
|
|
7118
|
+
)
|
|
6934
7119
|
] })
|
|
6935
7120
|
]
|
|
6936
7121
|
}
|
|
@@ -7163,8 +7348,11 @@ function FilterChip({ chip }) {
|
|
|
7163
7348
|
);
|
|
7164
7349
|
}
|
|
7165
7350
|
function NoteFilterBar() {
|
|
7166
|
-
const { filter, setFilter, noteCounts, notes, t } = useQa();
|
|
7351
|
+
const { filter, setFilter, noteCounts, notes, deleteNotes, t } = useQa();
|
|
7352
|
+
const [confirmPrune, setConfirmPrune] = useState(false);
|
|
7167
7353
|
if (notes.length < 3) return null;
|
|
7354
|
+
const others = notes.filter((n) => (n.status ?? "open") !== "fixed");
|
|
7355
|
+
const showPrune = filter.status === "fixed" && noteCounts.fixed > 0 && others.length > 0;
|
|
7168
7356
|
const sev = (key) => () => setFilter({ severity: filter.severity === key ? "all" : key });
|
|
7169
7357
|
const stat = (key) => () => setFilter({ status: filter.status === key ? "all" : key });
|
|
7170
7358
|
const chips = [
|
|
@@ -7269,7 +7457,48 @@ function NoteFilterBar() {
|
|
|
7269
7457
|
]
|
|
7270
7458
|
}
|
|
7271
7459
|
),
|
|
7272
|
-
/* @__PURE__ */ jsx("div", { className: "qa-flex qa-flex-wrap qa-gap-1", children: chips.map((chip) => /* @__PURE__ */ jsx(FilterChip, { chip }, chip.key)) })
|
|
7460
|
+
/* @__PURE__ */ jsx("div", { className: "qa-flex qa-flex-wrap qa-gap-1", children: chips.map((chip) => /* @__PURE__ */ jsx(FilterChip, { chip }, chip.key)) }),
|
|
7461
|
+
showPrune && /* @__PURE__ */ jsx("div", { className: "qa-text-11", "data-qa-prune": "true", children: confirmPrune ? /* @__PURE__ */ jsxs("span", { className: "qa-text-mid", children: [
|
|
7462
|
+
t("keep_only_retest_q", { n: others.length }),
|
|
7463
|
+
" ",
|
|
7464
|
+
/* @__PURE__ */ jsx(
|
|
7465
|
+
"button",
|
|
7466
|
+
{
|
|
7467
|
+
type: "button",
|
|
7468
|
+
onClick: () => {
|
|
7469
|
+
void deleteNotes(others.map((n) => n.id));
|
|
7470
|
+
setConfirmPrune(false);
|
|
7471
|
+
},
|
|
7472
|
+
className: "qa-font-semibold qa-text-danger qa-tap",
|
|
7473
|
+
style: { background: "transparent", border: "none", cursor: "pointer" },
|
|
7474
|
+
children: t("yes")
|
|
7475
|
+
}
|
|
7476
|
+
),
|
|
7477
|
+
" / ",
|
|
7478
|
+
/* @__PURE__ */ jsx(
|
|
7479
|
+
"button",
|
|
7480
|
+
{
|
|
7481
|
+
type: "button",
|
|
7482
|
+
onClick: () => setConfirmPrune(false),
|
|
7483
|
+
className: "qa-text-accent qa-tap",
|
|
7484
|
+
style: { background: "transparent", border: "none", cursor: "pointer" },
|
|
7485
|
+
children: t("no")
|
|
7486
|
+
}
|
|
7487
|
+
)
|
|
7488
|
+
] }) : /* @__PURE__ */ jsxs(
|
|
7489
|
+
"button",
|
|
7490
|
+
{
|
|
7491
|
+
type: "button",
|
|
7492
|
+
onClick: () => setConfirmPrune(true),
|
|
7493
|
+
title: t("keep_only_retest_hint"),
|
|
7494
|
+
className: "qa-tap qa-inline-flex qa-items-center qa-gap-1 qa-text-lo",
|
|
7495
|
+
style: { background: "transparent", border: "none", cursor: "pointer" },
|
|
7496
|
+
children: [
|
|
7497
|
+
/* @__PURE__ */ jsx(Icon, { name: "Trash", size: 11 }),
|
|
7498
|
+
t("keep_only_retest", { n: noteCounts.fixed })
|
|
7499
|
+
]
|
|
7500
|
+
}
|
|
7501
|
+
) })
|
|
7273
7502
|
] });
|
|
7274
7503
|
}
|
|
7275
7504
|
function Section({
|
|
@@ -7805,249 +8034,9 @@ function CredentialsSection() {
|
|
|
7805
8034
|
})
|
|
7806
8035
|
] });
|
|
7807
8036
|
}
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
group,
|
|
7812
|
-
checked,
|
|
7813
|
-
toggle,
|
|
7814
|
-
pick: pick2,
|
|
7815
|
-
onWalkFrom
|
|
7816
|
-
}) {
|
|
7817
|
-
const { lang, t, guideFailed, evidenceByStep } = useQa();
|
|
7818
|
-
const { id, color = DEFAULT_LANE_COLOR, steps: steps2 } = group;
|
|
7819
|
-
const done = steps2.filter((s) => checked.has(keyOf(id, s.path))).length;
|
|
7820
|
-
const pct = steps2.length > 0 ? Math.round(done / steps2.length * 100) : 0;
|
|
7821
|
-
const uncoveredRedCount = steps2.filter(
|
|
7822
|
-
(s) => s.risk === "red" && !checked.has(keyOf(id, s.path))
|
|
7823
|
-
).length;
|
|
7824
|
-
return /* @__PURE__ */ jsxs("div", { className: "qa-rounded-xl qa-border qa-border-subtle qa-bg-2 qa-p-3 qa-elev-1", children: [
|
|
7825
|
-
/* @__PURE__ */ jsxs("div", { className: "qa-mb-2 qa-flex qa-items-center qa-gap-2", children: [
|
|
7826
|
-
/* @__PURE__ */ jsx(
|
|
7827
|
-
"span",
|
|
7828
|
-
{
|
|
7829
|
-
className: "qa-h-2.5 qa-w-2.5 qa-rounded-full",
|
|
7830
|
-
style: { background: color }
|
|
7831
|
-
}
|
|
7832
|
-
),
|
|
7833
|
-
/* @__PURE__ */ jsx("span", { className: "qa-text-sm qa-font-bold qa-text-hi", children: pick2(group.role) }),
|
|
7834
|
-
/* @__PURE__ */ jsxs("span", { className: "qa-ms-auto qa-text-11 qa-font-medium qa-text-slate-400", children: [
|
|
7835
|
-
done,
|
|
7836
|
-
"/",
|
|
7837
|
-
steps2.length
|
|
7838
|
-
] }),
|
|
7839
|
-
uncoveredRedCount > 0 && /* @__PURE__ */ jsx(
|
|
7840
|
-
"span",
|
|
7841
|
-
{
|
|
7842
|
-
className: "qa-bg-danger-tint qa-text-danger qa-rounded qa-px-1 qa-text-10 qa-font-medium",
|
|
7843
|
-
title: lang === "ar" ? `${uncoveredRedCount} \u0645\u0646\u0637\u0642\u0629 \u062D\u0645\u0631\u0627\u0621 \u063A\u064A\u0631 \u0645\u063A\u0637\u0627\u0629` : `${uncoveredRedCount} uncovered red zone(s)`,
|
|
7844
|
-
children: lang === "ar" ? `\u0623\u062D\u0645\u0631: ${uncoveredRedCount}` : `red: ${uncoveredRedCount}`
|
|
7845
|
-
}
|
|
7846
|
-
)
|
|
7847
|
-
] }),
|
|
7848
|
-
/* @__PURE__ */ jsx(
|
|
7849
|
-
"div",
|
|
7850
|
-
{
|
|
7851
|
-
className: "qa-mb-3 qa-h-1.5 qa-overflow-hidden qa-rounded-full",
|
|
7852
|
-
style: { background: `${color}22` },
|
|
7853
|
-
children: /* @__PURE__ */ jsx(
|
|
7854
|
-
"div",
|
|
7855
|
-
{
|
|
7856
|
-
className: "qa-h-full qa-rounded-full qa-transition-all",
|
|
7857
|
-
style: { width: `${pct}%`, background: color }
|
|
7858
|
-
}
|
|
7859
|
-
)
|
|
7860
|
-
}
|
|
7861
|
-
),
|
|
7862
|
-
/* @__PURE__ */ jsxs("ol", { className: "qa-relative qa-ms-1.5", children: [
|
|
7863
|
-
/* @__PURE__ */ jsx(
|
|
7864
|
-
"span",
|
|
7865
|
-
{
|
|
7866
|
-
className: "qa-absolute qa-top-1 qa-bottom-0 qa-w-px",
|
|
7867
|
-
style: { insetInlineStart: "7px", background: `${color}40`, bottom: "4px" }
|
|
7868
|
-
}
|
|
7869
|
-
),
|
|
7870
|
-
steps2.map((s, i) => {
|
|
7871
|
-
const k = keyOf(id, s.path);
|
|
7872
|
-
const on = checked.has(k);
|
|
7873
|
-
const failed = guideFailed.has(k);
|
|
7874
|
-
const evidence = evidenceByStep.get(k);
|
|
7875
|
-
const evidenceCount = evidence ? evidence.length : 0;
|
|
7876
|
-
const riskColor = s.risk ? RISK_COLORS[s.risk] : RISK_COLORS.none;
|
|
7877
|
-
const dotTitle = !s.risk ? lang === "ar" ? "\u0644\u0645 \u064A\u062A\u0645 \u062A\u0642\u064A\u064A\u0645 \u0627\u0644\u0645\u062E\u0627\u0637\u0631 \u0628\u0639\u062F" : "not graded yet" : s.riskWhy ?? s.risk;
|
|
7878
|
-
return /* @__PURE__ */ jsxs("li", { className: "qa-relative qa-mb-2 qa-last-mb-0", children: [
|
|
7879
|
-
/* @__PURE__ */ jsx(
|
|
7880
|
-
"button",
|
|
7881
|
-
{
|
|
7882
|
-
onClick: () => onWalkFrom(k),
|
|
7883
|
-
title: t("walk_take_me", { path: s.path }),
|
|
7884
|
-
"aria-label": t("walk_take_me", { path: s.path }),
|
|
7885
|
-
className: "qa-tap-icon qa-absolute qa-rounded-lg qa-text-mid qa-hover-text-slate-600",
|
|
7886
|
-
style: {
|
|
7887
|
-
top: 0,
|
|
7888
|
-
insetInlineEnd: 0,
|
|
7889
|
-
background: "transparent",
|
|
7890
|
-
border: "none",
|
|
7891
|
-
cursor: "pointer",
|
|
7892
|
-
zIndex: 2
|
|
7893
|
-
},
|
|
7894
|
-
children: /* @__PURE__ */ jsx(Icon, { name: "Play", size: 12 })
|
|
7895
|
-
}
|
|
7896
|
-
),
|
|
7897
|
-
/* @__PURE__ */ jsxs(
|
|
7898
|
-
"button",
|
|
7899
|
-
{
|
|
7900
|
-
onClick: () => toggle(k),
|
|
7901
|
-
className: `qa-flex qa-w-full qa-items-start qa-gap-2.5 qa-rounded-lg qa-p-1 qa-text-start qa-hover-bg-black-3${failed ? " qa-bg-danger-tint" : ""}`,
|
|
7902
|
-
style: { background: failed ? void 0 : "transparent", border: "none", cursor: "pointer" },
|
|
7903
|
-
children: [
|
|
7904
|
-
/* @__PURE__ */ jsxs(
|
|
7905
|
-
"span",
|
|
7906
|
-
{
|
|
7907
|
-
className: "qa-relative qa-z-1 qa-mt-0.5 qa-flex qa-h-4 qa-w-4 qa-shrink-0 qa-items-center qa-justify-center qa-rounded-full qa-border-2 qa-transition",
|
|
7908
|
-
style: {
|
|
7909
|
-
borderColor: failed ? "var(--qa-danger)" : color,
|
|
7910
|
-
background: on ? color : failed ? "var(--qa-danger-tint)" : "var(--qa-surface-1)",
|
|
7911
|
-
zIndex: 1
|
|
7912
|
-
},
|
|
7913
|
-
children: [
|
|
7914
|
-
on && /* @__PURE__ */ jsx(Icon, { name: "Check", size: 10, strokeWidth: 3, className: "qa-text-white" }),
|
|
7915
|
-
!on && failed && /* @__PURE__ */ jsx(Icon, { name: "AlertTriangle", size: 9, strokeWidth: 2.5, className: "qa-text-danger" })
|
|
7916
|
-
]
|
|
7917
|
-
}
|
|
7918
|
-
),
|
|
7919
|
-
/* @__PURE__ */ jsxs("span", { className: "qa-min-w-0", children: [
|
|
7920
|
-
/* @__PURE__ */ jsxs("span", { className: "qa-flex qa-items-center qa-gap-1", children: [
|
|
7921
|
-
/* @__PURE__ */ jsx(
|
|
7922
|
-
"code",
|
|
7923
|
-
{
|
|
7924
|
-
className: "qa-rounded qa-px-1 qa-text-11 qa-font-semibold qa-dir-ltr qa-text-hi",
|
|
7925
|
-
style: {
|
|
7926
|
-
background: failed ? "var(--qa-danger-tint)" : `${color}14`,
|
|
7927
|
-
textDecoration: on ? "line-through" : "none",
|
|
7928
|
-
opacity: on ? 0.55 : 1
|
|
7929
|
-
},
|
|
7930
|
-
children: s.path
|
|
7931
|
-
}
|
|
7932
|
-
),
|
|
7933
|
-
/* @__PURE__ */ jsx(
|
|
7934
|
-
"span",
|
|
7935
|
-
{
|
|
7936
|
-
className: "qa-inline-block qa-rounded-full qa-shrink-0",
|
|
7937
|
-
style: {
|
|
7938
|
-
width: "6px",
|
|
7939
|
-
height: "6px",
|
|
7940
|
-
background: riskColor,
|
|
7941
|
-
flexShrink: 0
|
|
7942
|
-
},
|
|
7943
|
-
title: dotTitle
|
|
7944
|
-
}
|
|
7945
|
-
)
|
|
7946
|
-
] }),
|
|
7947
|
-
/* @__PURE__ */ jsx(
|
|
7948
|
-
"span",
|
|
7949
|
-
{
|
|
7950
|
-
className: "qa-mt-0.5 qa-block qa-text-11 qa-leading-relaxed qa-text-slate-500",
|
|
7951
|
-
style: { opacity: on ? 0.5 : 1 },
|
|
7952
|
-
children: pick2(s.what)
|
|
7953
|
-
}
|
|
7954
|
-
),
|
|
7955
|
-
evidenceCount > 0 && /* @__PURE__ */ jsx("span", { className: "qa-mt-1 qa-inline-flex qa-items-center qa-rounded qa-bg-accent-tint qa-text-accent qa-px-1 qa-text-10 qa-font-medium", children: t("evidence_n", { n: evidenceCount }) }),
|
|
7956
|
-
evidenceCount === 0 && on && /* @__PURE__ */ jsx("span", { className: "qa-mt-1 qa-inline-flex qa-items-center qa-rounded qa-bg-warn-tint qa-text-warn qa-px-1 qa-text-10 qa-font-medium", children: t("no_evidence") })
|
|
7957
|
-
] })
|
|
7958
|
-
]
|
|
7959
|
-
}
|
|
7960
|
-
)
|
|
7961
|
-
] }, `${k}-${i}`);
|
|
7962
|
-
})
|
|
7963
|
-
] })
|
|
7964
|
-
] });
|
|
7965
|
-
}
|
|
7966
|
-
function GuideSection() {
|
|
7967
|
-
const {
|
|
7968
|
-
guideChecked,
|
|
7969
|
-
toggleGuide,
|
|
7970
|
-
t,
|
|
7971
|
-
journey,
|
|
7972
|
-
journeyIsGeneric,
|
|
7973
|
-
pick: pick2,
|
|
7974
|
-
lang,
|
|
7975
|
-
startTestAlong,
|
|
7976
|
-
startWalk,
|
|
7977
|
-
testAlongSteps
|
|
7978
|
-
} = useQa();
|
|
7979
|
-
const all = journey.flatMap((g) => g.steps.map((s) => keyOf(g.id, s.path)));
|
|
7980
|
-
const done = all.filter((k) => guideChecked.has(k)).length;
|
|
7981
|
-
const pct = all.length > 0 ? Math.round(done / all.length * 100) : 0;
|
|
7982
|
-
const coverage = computeCoverage(journey, guideChecked);
|
|
7983
|
-
return /* @__PURE__ */ jsxs("div", { className: "qa-space-y-3", children: [
|
|
7984
|
-
journeyIsGeneric && /* @__PURE__ */ jsx(
|
|
7985
|
-
"p",
|
|
7986
|
-
{
|
|
7987
|
-
className: "qa-m-0 qa-rounded-xl qa-border qa-border-dashed qa-border-subtle qa-p-2 qa-text-10 qa-text-mid qa-leading-relaxed",
|
|
7988
|
-
"data-qa-generic-plan": "true",
|
|
7989
|
-
children: t("journey_generic")
|
|
7990
|
-
}
|
|
7991
|
-
),
|
|
7992
|
-
/* @__PURE__ */ jsxs("div", { className: "qa-rounded-xl qa-border qa-border-accent qa-bg-accent-tint qa-p-3 qa-elev-1", children: [
|
|
7993
|
-
coverage.red.total > 0 && /* @__PURE__ */ jsxs("div", { className: "qa-mb-1 qa-flex qa-items-center qa-gap-1.5 qa-text-11", children: [
|
|
7994
|
-
/* @__PURE__ */ jsx("span", { className: "qa-bg-danger-tint qa-text-danger qa-rounded qa-px-1 qa-font-bold", children: lang === "ar" ? "\u0623\u062D\u0645\u0631" : "RED" }),
|
|
7995
|
-
/* @__PURE__ */ jsxs("span", { className: "qa-dir-ltr qa-font-semibold qa-text-hi", children: [
|
|
7996
|
-
coverage.red.covered,
|
|
7997
|
-
"/",
|
|
7998
|
-
coverage.red.total,
|
|
7999
|
-
" ",
|
|
8000
|
-
lang === "ar" ? "\u0645\u063A\u0637\u0649" : "covered"
|
|
8001
|
-
] })
|
|
8002
|
-
] }),
|
|
8003
|
-
/* @__PURE__ */ jsxs("div", { className: "qa-flex qa-items-center qa-justify-between qa-text-sm qa-font-semibold qa-text-hi", children: [
|
|
8004
|
-
/* @__PURE__ */ jsx("span", { children: t("journey_title") }),
|
|
8005
|
-
/* @__PURE__ */ jsxs("span", { className: "qa-dir-ltr", children: [
|
|
8006
|
-
done,
|
|
8007
|
-
"/",
|
|
8008
|
-
all.length,
|
|
8009
|
-
" \xB7 ",
|
|
8010
|
-
pct,
|
|
8011
|
-
"%"
|
|
8012
|
-
] })
|
|
8013
|
-
] }),
|
|
8014
|
-
/* @__PURE__ */ jsx("div", { className: "qa-mt-2 qa-h-2 qa-overflow-hidden qa-rounded-full qa-bg-3", children: /* @__PURE__ */ jsx(
|
|
8015
|
-
"div",
|
|
8016
|
-
{
|
|
8017
|
-
className: "qa-h-full qa-rounded-full qa-bg-accent qa-transition-all",
|
|
8018
|
-
style: { width: `${pct}%` }
|
|
8019
|
-
}
|
|
8020
|
-
) }),
|
|
8021
|
-
/* @__PURE__ */ jsx("div", { className: "qa-mt-2 qa-flex qa-justify-end", children: /* @__PURE__ */ jsxs(
|
|
8022
|
-
"button",
|
|
8023
|
-
{
|
|
8024
|
-
type: "button",
|
|
8025
|
-
onClick: startTestAlong,
|
|
8026
|
-
disabled: all.length === 0,
|
|
8027
|
-
className: "qa-tap qa-inline-flex qa-items-center qa-gap-1.5 qa-rounded-full qa-bg-accent qa-border-0 qa-cursor-pointer qa-px-3 qa-py-1 qa-text-xs qa-font-semibold qa-focus-ring",
|
|
8028
|
-
children: [
|
|
8029
|
-
/* @__PURE__ */ jsx(Icon, { name: "Play", size: 13 }),
|
|
8030
|
-
t("start_walkthrough")
|
|
8031
|
-
]
|
|
8032
|
-
}
|
|
8033
|
-
) })
|
|
8034
|
-
] }),
|
|
8035
|
-
journey.map((g) => /* @__PURE__ */ jsx(
|
|
8036
|
-
Lane,
|
|
8037
|
-
{
|
|
8038
|
-
group: g,
|
|
8039
|
-
checked: guideChecked,
|
|
8040
|
-
toggle: toggleGuide,
|
|
8041
|
-
onWalkFrom: (key) => {
|
|
8042
|
-
const at = testAlongSteps.findIndex((step) => step.key === key);
|
|
8043
|
-
startWalk("plan", at === -1 ? 0 : at);
|
|
8044
|
-
},
|
|
8045
|
-
pick: pick2
|
|
8046
|
-
},
|
|
8047
|
-
g.id
|
|
8048
|
-
))
|
|
8049
|
-
] });
|
|
8050
|
-
}
|
|
8037
|
+
|
|
8038
|
+
// src/lib/features.ts
|
|
8039
|
+
var GUIDE_TAB_ENABLED = false;
|
|
8051
8040
|
function isCoarsePointer() {
|
|
8052
8041
|
if (typeof window === "undefined") return false;
|
|
8053
8042
|
try {
|
|
@@ -8071,11 +8060,15 @@ function useCoarsePointer() {
|
|
|
8071
8060
|
}, []);
|
|
8072
8061
|
return coarse;
|
|
8073
8062
|
}
|
|
8074
|
-
var
|
|
8063
|
+
var ALL_TABS = [
|
|
8075
8064
|
{ key: "notes", labelKey: "tab_notes", icon: "StickyNote" },
|
|
8076
8065
|
{ key: "logins", labelKey: "tab_logins", icon: "KeyRound" },
|
|
8077
8066
|
{ key: "guide", labelKey: "tab_guide", icon: "Map" }
|
|
8078
8067
|
];
|
|
8068
|
+
var TABS = ALL_TABS.filter((tab) => tab.key !== "guide" || GUIDE_TAB_ENABLED);
|
|
8069
|
+
function visibleTab(tab) {
|
|
8070
|
+
return tab === "guide" && !GUIDE_TAB_ENABLED ? "notes" : tab;
|
|
8071
|
+
}
|
|
8079
8072
|
function todayName() {
|
|
8080
8073
|
return `qa-notes-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
|
|
8081
8074
|
}
|
|
@@ -8110,7 +8103,7 @@ var PANEL_TRANSITION_WITH_LIFT = "opacity 200ms cubic-bezier(0.4,0,0.2,1), trans
|
|
|
8110
8103
|
function QaPanel() {
|
|
8111
8104
|
const {
|
|
8112
8105
|
isOpen,
|
|
8113
|
-
activeTab,
|
|
8106
|
+
activeTab: storedTab,
|
|
8114
8107
|
setActiveTab,
|
|
8115
8108
|
notes,
|
|
8116
8109
|
exportZip,
|
|
@@ -8141,6 +8134,7 @@ function QaPanel() {
|
|
|
8141
8134
|
panelCollapsed,
|
|
8142
8135
|
setPanelCollapsed
|
|
8143
8136
|
} = useQa();
|
|
8137
|
+
const activeTab = visibleTab(storedTab);
|
|
8144
8138
|
const [confirmClear, setConfirmClear] = useState(false);
|
|
8145
8139
|
const [naming, setNaming] = useState(false);
|
|
8146
8140
|
const [filename, setFilename] = useState("");
|
|
@@ -8508,7 +8502,7 @@ function QaPanel() {
|
|
|
8508
8502
|
) })
|
|
8509
8503
|
] }),
|
|
8510
8504
|
!simpleMode && activeTab === "logins" && /* @__PURE__ */ jsx(CredentialsSection, {}),
|
|
8511
|
-
!simpleMode &&
|
|
8505
|
+
!simpleMode && GUIDE_TAB_ENABLED
|
|
8512
8506
|
] })
|
|
8513
8507
|
] }),
|
|
8514
8508
|
settingsOpen && /* @__PURE__ */ jsx(SettingsSheet, { onClose: () => setSettingsOpen(false) }),
|
|
@@ -8715,6 +8709,20 @@ var RISK_COLOR = {
|
|
|
8715
8709
|
amber: "var(--qa-warn)",
|
|
8716
8710
|
green: "var(--qa-success)"
|
|
8717
8711
|
};
|
|
8712
|
+
function useCurrentPath() {
|
|
8713
|
+
const [path, setPath] = useState(() => typeof window === "undefined" ? "" : window.location.pathname);
|
|
8714
|
+
useEffect(() => {
|
|
8715
|
+
if (typeof window === "undefined") return;
|
|
8716
|
+
const sync = () => setPath(window.location.pathname);
|
|
8717
|
+
window.addEventListener("popstate", sync);
|
|
8718
|
+
window.addEventListener("hashchange", sync);
|
|
8719
|
+
return () => {
|
|
8720
|
+
window.removeEventListener("popstate", sync);
|
|
8721
|
+
window.removeEventListener("hashchange", sync);
|
|
8722
|
+
};
|
|
8723
|
+
}, []);
|
|
8724
|
+
return path;
|
|
8725
|
+
}
|
|
8718
8726
|
function WalkHud() {
|
|
8719
8727
|
const {
|
|
8720
8728
|
dir,
|
|
@@ -8734,10 +8742,13 @@ function WalkHud() {
|
|
|
8734
8742
|
startCapture,
|
|
8735
8743
|
updateNote,
|
|
8736
8744
|
retestNote,
|
|
8737
|
-
notes
|
|
8745
|
+
notes,
|
|
8746
|
+
notesLoading
|
|
8738
8747
|
} = useQa();
|
|
8739
8748
|
const index = Math.min(walk.index, Math.max(0, walkStops.length - 1));
|
|
8740
8749
|
const stop = walkStops[index];
|
|
8750
|
+
const currentPath = useCurrentPath();
|
|
8751
|
+
const [going, setGoing] = useState(false);
|
|
8741
8752
|
const backIcon = dir === "rtl" ? "ChevronRight" : "ChevronLeft";
|
|
8742
8753
|
const nextIcon = dir === "rtl" ? "ChevronLeft" : "ChevronRight";
|
|
8743
8754
|
useEffect(() => {
|
|
@@ -8751,8 +8762,9 @@ function WalkHud() {
|
|
|
8751
8762
|
if (latest?.target) flashLocate(latest.target);
|
|
8752
8763
|
}, [index, walk.source]);
|
|
8753
8764
|
useEffect(() => {
|
|
8765
|
+
if (notesLoading) return;
|
|
8754
8766
|
if (walk.active && walkStops.length === 0) exitWalk();
|
|
8755
|
-
}, [walk.active, walkStops.length, exitWalk]);
|
|
8767
|
+
}, [walk.active, walkStops.length, exitWalk, notesLoading]);
|
|
8756
8768
|
if (!walk.active || !stop) return null;
|
|
8757
8769
|
const atLast = index >= walkStops.length - 1;
|
|
8758
8770
|
let title;
|
|
@@ -8913,7 +8925,7 @@ function WalkHud() {
|
|
|
8913
8925
|
)
|
|
8914
8926
|
] });
|
|
8915
8927
|
}
|
|
8916
|
-
const onThisPage = stop.path === ANY_PAGE ||
|
|
8928
|
+
const onThisPage = stop.path === ANY_PAGE || stop.path === currentPath;
|
|
8917
8929
|
return /* @__PURE__ */ jsxs(
|
|
8918
8930
|
"div",
|
|
8919
8931
|
{
|
|
@@ -8949,12 +8961,24 @@ function WalkHud() {
|
|
|
8949
8961
|
"button",
|
|
8950
8962
|
{
|
|
8951
8963
|
type: "button",
|
|
8952
|
-
|
|
8964
|
+
disabled: going,
|
|
8965
|
+
onClick: () => {
|
|
8966
|
+
setGoing(true);
|
|
8967
|
+
walkNavigate(stop.path);
|
|
8968
|
+
window.setTimeout(() => setGoing(false), 900);
|
|
8969
|
+
},
|
|
8953
8970
|
className: "qa-tap qa-inline-flex qa-flex-1 qa-items-center qa-justify-center qa-gap-1.5 qa-rounded-lg qa-bg-accent qa-px-2 qa-py-1.5 qa-text-11 qa-font-semibold",
|
|
8954
|
-
style: { border: "none", cursor: "pointer" },
|
|
8971
|
+
style: { border: "none", cursor: going ? "default" : "pointer" },
|
|
8955
8972
|
children: [
|
|
8956
|
-
/* @__PURE__ */ jsx(
|
|
8957
|
-
|
|
8973
|
+
/* @__PURE__ */ jsx(
|
|
8974
|
+
Icon,
|
|
8975
|
+
{
|
|
8976
|
+
name: going ? "Loader2" : "MapPinned",
|
|
8977
|
+
size: 13,
|
|
8978
|
+
className: going ? "qa-animate-spin" : void 0
|
|
8979
|
+
}
|
|
8980
|
+
),
|
|
8981
|
+
going ? t("walk_taking_you") : t("walk_take_me", { path: stop.path })
|
|
8958
8982
|
]
|
|
8959
8983
|
}
|
|
8960
8984
|
),
|
|
@@ -10365,5 +10389,5 @@ function Qapture({ config }) {
|
|
|
10365
10389
|
}
|
|
10366
10390
|
|
|
10367
10391
|
export { Qapture, deleteQaDatabase, initQaStudio };
|
|
10368
|
-
//# sourceMappingURL=chunk-
|
|
10369
|
-
//# sourceMappingURL=chunk-
|
|
10392
|
+
//# sourceMappingURL=chunk-G6O4SIEH.js.map
|
|
10393
|
+
//# sourceMappingURL=chunk-G6O4SIEH.js.map
|