@sirendesign/markup 1.0.13 → 1.0.14
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/components/FeedbackPins.d.ts +7 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/index.esm.js +196 -28
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +196 -28
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/index.d.ts +16 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -851,6 +851,7 @@ const initialState = {
|
|
|
851
851
|
currentUser: null,
|
|
852
852
|
isAuthenticated: false,
|
|
853
853
|
viewportMode: null,
|
|
854
|
+
showPins: true,
|
|
854
855
|
};
|
|
855
856
|
const useMarkupStore = create()(persist((set, get) => ({
|
|
856
857
|
...initialState,
|
|
@@ -913,6 +914,7 @@ const useMarkupStore = create()(persist((set, get) => ({
|
|
|
913
914
|
activeTab: 'create',
|
|
914
915
|
activeTool: null,
|
|
915
916
|
}),
|
|
917
|
+
setShowPins: (show) => set({ showPins: show }),
|
|
916
918
|
}), {
|
|
917
919
|
name: 'markup-storage',
|
|
918
920
|
storage: createJSONStorage(() => localStorage),
|
|
@@ -17034,6 +17036,7 @@ const deleteScreenRecording = async (downloadURL) => {
|
|
|
17034
17036
|
}
|
|
17035
17037
|
};
|
|
17036
17038
|
|
|
17039
|
+
const MessageIcon = ({ className, ...props }) => (jsxRuntime.jsx("svg", { className: className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: jsxRuntime.jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) }));
|
|
17037
17040
|
const CloseIcon = ({ className, ...props }) => (jsxRuntime.jsxs("svg", { className: className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }));
|
|
17038
17041
|
const CameraIcon = ({ className, ...props }) => (jsxRuntime.jsxs("svg", { className: className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [jsxRuntime.jsx("path", { d: "M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z" }), jsxRuntime.jsx("circle", { cx: "12", cy: "13", r: "4" })] }));
|
|
17039
17042
|
const CircleIcon = ({ className, ...props }) => (jsxRuntime.jsx("svg", { className: className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }) }));
|
|
@@ -17376,7 +17379,7 @@ const AnnotationOverlay = ({ screenshot, onComplete, onCancel, }) => {
|
|
|
17376
17379
|
|
|
17377
17380
|
const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastClickedElement, lastClickedText, }) => {
|
|
17378
17381
|
var _a, _b;
|
|
17379
|
-
const { config, currentScreenshot, setCurrentScreenshot, annotations, setAnnotations, clearAnnotations, setIsCapturing, isCapturing, currentUser, } = useMarkupStore();
|
|
17382
|
+
const { config, currentScreenshot, setCurrentScreenshot, annotations, setAnnotations, clearAnnotations, setIsCapturing, isCapturing, currentUser, setIsOpen, } = useMarkupStore();
|
|
17380
17383
|
const [title, setTitle] = require$$0.useState((initialData === null || initialData === void 0 ? void 0 : initialData.title) || "");
|
|
17381
17384
|
const [description, setDescription] = require$$0.useState((initialData === null || initialData === void 0 ? void 0 : initialData.description) || "");
|
|
17382
17385
|
const [priority, setPriority] = require$$0.useState((initialData === null || initialData === void 0 ? void 0 : initialData.priority) || "medium");
|
|
@@ -17395,12 +17398,48 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17395
17398
|
const [isUploadingVideo, setIsUploadingVideo] = require$$0.useState(false);
|
|
17396
17399
|
const [mediaRecorder, setMediaRecorder] = require$$0.useState(null);
|
|
17397
17400
|
const [recordingStream, setRecordingStream] = require$$0.useState(null);
|
|
17401
|
+
// Pin placement states
|
|
17402
|
+
const [isPlacingPin, setIsPlacingPin] = require$$0.useState(false);
|
|
17403
|
+
const [pinPosition, setPinPosition] = require$$0.useState((initialData === null || initialData === void 0 ? void 0 : initialData.pinLocation)
|
|
17404
|
+
? {
|
|
17405
|
+
x: initialData.pinLocation.x,
|
|
17406
|
+
y: initialData.pinLocation.y,
|
|
17407
|
+
pageX: initialData.pinLocation.pageX,
|
|
17408
|
+
pageY: initialData.pinLocation.pageY,
|
|
17409
|
+
}
|
|
17410
|
+
: null);
|
|
17398
17411
|
// Load initial annotations into store if editing
|
|
17399
17412
|
require$$0.useEffect(() => {
|
|
17400
17413
|
if (initialData === null || initialData === void 0 ? void 0 : initialData.annotations) {
|
|
17401
17414
|
setAnnotations(initialData.annotations);
|
|
17402
17415
|
}
|
|
17403
17416
|
}, [initialData, setAnnotations]);
|
|
17417
|
+
// Handle pin placement mode
|
|
17418
|
+
require$$0.useEffect(() => {
|
|
17419
|
+
if (!isPlacingPin)
|
|
17420
|
+
return;
|
|
17421
|
+
// Close widget during pin placement
|
|
17422
|
+
setIsOpen(false);
|
|
17423
|
+
const handlePinClick = (e) => {
|
|
17424
|
+
e.preventDefault();
|
|
17425
|
+
e.stopPropagation();
|
|
17426
|
+
setPinPosition({
|
|
17427
|
+
x: e.clientX,
|
|
17428
|
+
y: e.clientY,
|
|
17429
|
+
pageX: e.pageX,
|
|
17430
|
+
pageY: e.pageY,
|
|
17431
|
+
});
|
|
17432
|
+
setIsPlacingPin(false);
|
|
17433
|
+
};
|
|
17434
|
+
document.addEventListener('click', handlePinClick, true);
|
|
17435
|
+
document.body.style.cursor = 'crosshair';
|
|
17436
|
+
return () => {
|
|
17437
|
+
document.removeEventListener('click', handlePinClick, true);
|
|
17438
|
+
document.body.style.cursor = '';
|
|
17439
|
+
// Reopen widget when pin placement is done
|
|
17440
|
+
setIsOpen(true);
|
|
17441
|
+
};
|
|
17442
|
+
}, [isPlacingPin, setIsOpen]);
|
|
17404
17443
|
// Auto-capture selected text when component mounts (only if creating new)
|
|
17405
17444
|
require$$0.useEffect(() => {
|
|
17406
17445
|
var _a;
|
|
@@ -17438,12 +17477,12 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17438
17477
|
const handleStartRecording = async () => {
|
|
17439
17478
|
try {
|
|
17440
17479
|
const stream = await navigator.mediaDevices.getDisplayMedia({
|
|
17441
|
-
video: { mediaSource:
|
|
17480
|
+
video: { mediaSource: "screen" },
|
|
17442
17481
|
audio: false,
|
|
17443
17482
|
});
|
|
17444
17483
|
setRecordingStream(stream);
|
|
17445
17484
|
const recorder = new MediaRecorder(stream, {
|
|
17446
|
-
mimeType:
|
|
17485
|
+
mimeType: "video/webm;codecs=vp8",
|
|
17447
17486
|
});
|
|
17448
17487
|
const chunks = [];
|
|
17449
17488
|
recorder.ondataavailable = (e) => {
|
|
@@ -17452,14 +17491,14 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17452
17491
|
}
|
|
17453
17492
|
};
|
|
17454
17493
|
recorder.onstop = () => {
|
|
17455
|
-
const blob = new Blob(chunks, { type:
|
|
17494
|
+
const blob = new Blob(chunks, { type: "video/webm" });
|
|
17456
17495
|
// Store blob for upload and create preview URL
|
|
17457
17496
|
setRecordedBlob(blob);
|
|
17458
17497
|
const previewURL = URL.createObjectURL(blob);
|
|
17459
17498
|
setRecordedVideo(previewURL);
|
|
17460
17499
|
console.log(`Recording saved: ${(blob.size / 1048576).toFixed(2)}MB`);
|
|
17461
17500
|
// Stop all tracks
|
|
17462
|
-
stream.getTracks().forEach(track => track.stop());
|
|
17501
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
17463
17502
|
setRecordingStream(null);
|
|
17464
17503
|
};
|
|
17465
17504
|
recorder.start();
|
|
@@ -17467,25 +17506,25 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17467
17506
|
setIsRecording(true);
|
|
17468
17507
|
}
|
|
17469
17508
|
catch (error) {
|
|
17470
|
-
console.error(
|
|
17471
|
-
alert(
|
|
17509
|
+
console.error("Failed to start recording:", error);
|
|
17510
|
+
alert("Screen recording is not supported or permission was denied.");
|
|
17472
17511
|
}
|
|
17473
17512
|
};
|
|
17474
17513
|
const handleStopRecording = () => {
|
|
17475
|
-
if (mediaRecorder && mediaRecorder.state !==
|
|
17514
|
+
if (mediaRecorder && mediaRecorder.state !== "inactive") {
|
|
17476
17515
|
mediaRecorder.stop();
|
|
17477
17516
|
setIsRecording(false);
|
|
17478
17517
|
}
|
|
17479
17518
|
};
|
|
17480
17519
|
const handleDeleteRecording = () => {
|
|
17481
17520
|
// Revoke blob URL to free memory
|
|
17482
|
-
if (recordedVideo && recordedVideo.startsWith(
|
|
17521
|
+
if (recordedVideo && recordedVideo.startsWith("blob:")) {
|
|
17483
17522
|
URL.revokeObjectURL(recordedVideo);
|
|
17484
17523
|
}
|
|
17485
17524
|
setRecordedVideo(undefined);
|
|
17486
17525
|
setRecordedBlob(null);
|
|
17487
17526
|
if (recordingStream) {
|
|
17488
|
-
recordingStream.getTracks().forEach(track => track.stop());
|
|
17527
|
+
recordingStream.getTracks().forEach((track) => track.stop());
|
|
17489
17528
|
setRecordingStream(null);
|
|
17490
17529
|
}
|
|
17491
17530
|
};
|
|
@@ -17543,11 +17582,11 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17543
17582
|
setIsUploadingVideo(true);
|
|
17544
17583
|
const tempId = generateId();
|
|
17545
17584
|
screenRecordingURL = await uploadScreenRecording(recordedBlob, tempId);
|
|
17546
|
-
console.log(
|
|
17585
|
+
console.log("Video uploaded to Storage:", screenRecordingURL);
|
|
17547
17586
|
}
|
|
17548
17587
|
catch (error) {
|
|
17549
|
-
console.error(
|
|
17550
|
-
alert(
|
|
17588
|
+
console.error("Failed to upload video:", error);
|
|
17589
|
+
alert("Failed to upload screen recording. Please try again.");
|
|
17551
17590
|
setIsSubmitting(false);
|
|
17552
17591
|
setIsUploadingVideo(false);
|
|
17553
17592
|
return;
|
|
@@ -17556,10 +17595,13 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17556
17595
|
setIsUploadingVideo(false);
|
|
17557
17596
|
}
|
|
17558
17597
|
}
|
|
17598
|
+
const pageWidth = document.documentElement.scrollWidth;
|
|
17599
|
+
const pageHeight = document.documentElement.scrollHeight;
|
|
17559
17600
|
const feedback = {
|
|
17560
17601
|
id: (initialData === null || initialData === void 0 ? void 0 : initialData.id) || generateId(),
|
|
17561
17602
|
type: feedbackType,
|
|
17562
|
-
title: title.trim() ||
|
|
17603
|
+
title: title.trim() ||
|
|
17604
|
+
(feedbackType === "copy-amendment" ? "Copy Amendment" : ""),
|
|
17563
17605
|
description: description.trim(),
|
|
17564
17606
|
currentCopy: feedbackType === "copy-amendment"
|
|
17565
17607
|
? currentCopy.trim()
|
|
@@ -17569,10 +17611,12 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17569
17611
|
annotations: annotations,
|
|
17570
17612
|
status: (initialData === null || initialData === void 0 ? void 0 : initialData.status) || "open",
|
|
17571
17613
|
priority,
|
|
17572
|
-
pageMetadata: (initialData === null || initialData === void 0 ? void 0 : initialData.pageMetadata) ||
|
|
17614
|
+
pageMetadata: (initialData === null || initialData === void 0 ? void 0 : initialData.pageMetadata) ||
|
|
17615
|
+
getPageMetadata(lastClickedElement || undefined),
|
|
17573
17616
|
createdAt: (initialData === null || initialData === void 0 ? void 0 : initialData.createdAt) || new Date().toISOString(),
|
|
17574
17617
|
updatedAt: new Date().toISOString(),
|
|
17575
|
-
createdBy: (initialData === null || initialData === void 0 ? void 0 : initialData.createdBy) ||
|
|
17618
|
+
createdBy: (initialData === null || initialData === void 0 ? void 0 : initialData.createdBy) ||
|
|
17619
|
+
currentUser || {
|
|
17576
17620
|
id: ((_a = config.user) === null || _a === void 0 ? void 0 : _a.id) || "anonymous",
|
|
17577
17621
|
name: ((_b = config.user) === null || _b === void 0 ? void 0 : _b.name) || "Anonymous",
|
|
17578
17622
|
email: ((_c = config.user) === null || _c === void 0 ? void 0 : _c.email) || "",
|
|
@@ -17583,6 +17627,22 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17583
17627
|
sessionEvents: getSessionEvents ? getSessionEvents() : undefined,
|
|
17584
17628
|
designMockup,
|
|
17585
17629
|
screenRecording: screenRecordingURL,
|
|
17630
|
+
pinLocation: pinPosition
|
|
17631
|
+
? {
|
|
17632
|
+
x: pinPosition.x,
|
|
17633
|
+
y: pinPosition.y,
|
|
17634
|
+
pageX: pinPosition.pageX,
|
|
17635
|
+
pageY: pinPosition.pageY,
|
|
17636
|
+
viewportWidth: window.innerWidth,
|
|
17637
|
+
viewportHeight: window.innerHeight,
|
|
17638
|
+
pageWidth,
|
|
17639
|
+
pageHeight,
|
|
17640
|
+
scrollX: window.scrollX,
|
|
17641
|
+
scrollY: window.scrollY,
|
|
17642
|
+
percentX: pageWidth ? (pinPosition.pageX / pageWidth) * 100 : undefined,
|
|
17643
|
+
percentY: pageHeight ? (pinPosition.pageY / pageHeight) * 100 : undefined,
|
|
17644
|
+
}
|
|
17645
|
+
: undefined,
|
|
17586
17646
|
};
|
|
17587
17647
|
try {
|
|
17588
17648
|
if (config.onSubmit) {
|
|
@@ -17607,6 +17667,10 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17607
17667
|
if (isAnnotating && currentScreenshot) {
|
|
17608
17668
|
return (jsxRuntime.jsx(AnnotationOverlay, { screenshot: currentScreenshot, onComplete: handleAnnotationComplete, onCancel: handleAnnotationCancel }));
|
|
17609
17669
|
}
|
|
17670
|
+
// Show pin placement overlay
|
|
17671
|
+
if (isPlacingPin) {
|
|
17672
|
+
return (jsxRuntime.jsx("div", { className: "fixed top-4 left-1/2 -translate-x-1/2 z-[999999] pointer-events-none", children: jsxRuntime.jsxs("div", { className: "bg-white rounded-2xl shadow-2xl p-6 max-w-md mx-4 text-center border-2 border-orange-500", children: [jsxRuntime.jsx("div", { className: "w-12 h-12 mx-auto mb-3 bg-orange-100 rounded-full flex items-center justify-center", children: jsxRuntime.jsxs("svg", { className: "w-6 h-6 text-orange-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" }), jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M15 11a3 3 0 11-6 0 3 3 0 016 0z" })] }) }), jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-900 mb-2", children: "Click to Place Pin" }), jsxRuntime.jsx("p", { className: "text-gray-600 text-sm mb-3", children: "Click anywhere on the page to mark the location" }), jsxRuntime.jsx("button", { type: "button", onClick: () => setIsPlacingPin(false), className: "px-4 py-2 text-sm font-medium text-white bg-orange-600 hover:bg-orange-700 rounded-lg transition-colors pointer-events-auto", children: "Cancel" })] }) }));
|
|
17673
|
+
}
|
|
17610
17674
|
return (jsxRuntime.jsxs("form", { onSubmit: handleSubmit, children: [jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Type" }), jsxRuntime.jsx("div", { className: "flex gap-2", children: [
|
|
17611
17675
|
{ id: "general", label: "General" },
|
|
17612
17676
|
{ id: "copy-amendment", label: "Copy Change" },
|
|
@@ -17614,10 +17678,14 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17614
17678
|
? "bg-[#C2D1D9] text-black shadow-md shadow-[#C2D1D9]/30"
|
|
17615
17679
|
: "bg-gray-100 text-gray-700 hover:bg-gray-200 hover:shadow-sm"), children: type.label }, type.id))) })] }), feedbackType === "copy-amendment" && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Current Copy" }), jsxRuntime.jsx("textarea", { placeholder: "Select text on page or paste current copy here...", value: currentCopy, onChange: (e) => setCurrentCopy(e.target.value), className: "w-full px-4 py-3 bg-red-50 border border-red-200 rounded-xl text-sm min-h-[80px] resize-y focus:outline-none focus:bg-white focus:border-red-500 focus:shadow-lg focus:shadow-red-500/10 transition-all" })] }), jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "New Copy *" }), jsxRuntime.jsx("textarea", { placeholder: "Enter the replacement text...", value: newCopy, onChange: (e) => setNewCopy(e.target.value), required: feedbackType === "copy-amendment", className: "w-full px-4 py-3 bg-green-50 border border-green-200 rounded-xl text-sm min-h-[80px] resize-y focus:outline-none focus:bg-white focus:border-green-500 focus:shadow-lg focus:shadow-green-500/10 transition-all" })] })] })), feedbackType !== "copy-amendment" && (jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Title *" }), jsxRuntime.jsx("input", { type: "text", placeholder: "Brief description of the issue", value: title, onChange: (e) => setTitle(e.target.value), required: true, className: "w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl text-sm focus:outline-none focus:bg-white focus:border-[#C2D1D9] focus:shadow-lg focus:shadow-[#C2D1D9]/10 transition-all" })] })), feedbackType !== "copy-amendment" && (jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Description" }), jsxRuntime.jsx("textarea", { placeholder: "Provide more details about the issue...", value: description, onChange: (e) => setDescription(e.target.value), required: true, className: "w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl text-sm focus:outline-none focus:bg-white focus:border-[#C2D1D9] focus:shadow-lg focus:shadow-[#C2D1D9]/10 transition-all min-h-[100px] resize-y" })] })), jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Priority" }), jsxRuntime.jsx("div", { className: "flex gap-2", children: Object.keys(priorityColors).map((p) => (jsxRuntime.jsx("div", { onClick: () => setPriority(p), className: cn("flex-1 py-2 px-2 rounded-xl text-xs font-medium cursor-pointer transition-all text-center capitalize border border-transparent", priority === p
|
|
17616
17680
|
? cn(priorityColors[p], "border-transparent shadow-sm")
|
|
17617
|
-
: "bg-gray-100 text-gray-600 hover:bg-gray-200"), children: p }, p))) })] }), annotatedScreenshot || currentScreenshot ? (jsxRuntime.jsxs("div", { className: "relative rounded-xl overflow-hidden shadow-md mb-5 group", children: [jsxRuntime.jsx("img", { src: annotatedScreenshot || currentScreenshot || "", alt: "Screenshot", className: "w-full block" }), jsxRuntime.jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/40 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" }), jsxRuntime.jsxs("div", { className: "absolute top-3 right-3 flex gap-2", children: [jsxRuntime.jsxs("button", { type: "button", onClick: handleEditScreenshot, className: "bg-white text-gray-700 border-none px-3 py-1.5 rounded-lg cursor-pointer text-xs font-medium flex items-center gap-1.5 hover:bg-gray-100 shadow-lg transition-all", children: [jsxRuntime.jsx(EditIcon, { className: "w-3.5 h-3.5" }), " Edit"] }), jsxRuntime.jsxs("button", { type: "button", onClick: handleRemoveScreenshot, className: "bg-white text-red-600 border-none px-3 py-1.5 rounded-lg cursor-pointer text-xs font-medium flex items-center gap-1.5 hover:bg-red-50 shadow-lg transition-all", children: [jsxRuntime.jsx(TrashIcon, { className: "w-3.5 h-3.5" }), " Remove"] })] })] })) : (jsxRuntime.jsxs("div", { className: "flex gap-3 mb-5", children: [jsxRuntime.jsx("div", { onClick: handleCaptureScreenshot, className: "flex-1 p-4 bg-white border-2 border-dashed border-gray-300 rounded-xl cursor-pointer flex items-center justify-center gap-2.5 text-gray-600 text-sm font-medium hover:border-indigo-400 hover:text-indigo-600 hover:bg-indigo-50 hover:shadow-md transition-all disabled:opacity-50 disabled:cursor-not-allowed", children: isCapturing ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: "w-5 h-5 border-2 border-indigo-200 border-t-indigo-600 rounded-full animate-spin" }), "Capturing..."] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(CameraIcon, { className: "w-5 h-5" }), " Screenshot"] })) }), jsxRuntime.jsx("div", { onClick: isRecording
|
|
17681
|
+
: "bg-gray-100 text-gray-600 hover:bg-gray-200"), children: p }, p))) })] }), annotatedScreenshot || currentScreenshot ? (jsxRuntime.jsxs("div", { className: "relative rounded-xl overflow-hidden shadow-md mb-5 group", children: [jsxRuntime.jsx("img", { src: annotatedScreenshot || currentScreenshot || "", alt: "Screenshot", className: "w-full block" }), jsxRuntime.jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/40 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" }), jsxRuntime.jsxs("div", { className: "absolute top-3 right-3 flex gap-2", children: [jsxRuntime.jsxs("button", { type: "button", onClick: handleEditScreenshot, className: "bg-white text-gray-700 border-none px-3 py-1.5 rounded-lg cursor-pointer text-xs font-medium flex items-center gap-1.5 hover:bg-gray-100 shadow-lg transition-all", children: [jsxRuntime.jsx(EditIcon, { className: "w-3.5 h-3.5" }), " Edit"] }), jsxRuntime.jsxs("button", { type: "button", onClick: handleRemoveScreenshot, className: "bg-white text-red-600 border-none px-3 py-1.5 rounded-lg cursor-pointer text-xs font-medium flex items-center gap-1.5 hover:bg-red-50 shadow-lg transition-all", children: [jsxRuntime.jsx(TrashIcon, { className: "w-3.5 h-3.5" }), " Remove"] })] })] })) : (jsxRuntime.jsxs("div", { className: "flex gap-3 mb-5", children: [jsxRuntime.jsx("div", { onClick: handleCaptureScreenshot, className: "flex-1 p-4 bg-white border-2 border-dashed border-gray-300 rounded-xl cursor-pointer flex items-center justify-center gap-2.5 text-gray-600 text-sm font-medium hover:border-indigo-400 hover:text-indigo-600 hover:bg-indigo-50 hover:shadow-md transition-all disabled:opacity-50 disabled:cursor-not-allowed", children: isCapturing ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: "w-5 h-5 border-2 border-indigo-200 border-t-indigo-600 rounded-full animate-spin" }), "Capturing..."] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(CameraIcon, { className: "w-5 h-5" }), " Screenshot"] })) }), jsxRuntime.jsx("div", { onClick: isRecording
|
|
17682
|
+
? handleStopRecording
|
|
17683
|
+
: handleStartRecording, className: "flex-1 p-4 bg-white border-2 border-dashed border-gray-300 rounded-xl cursor-pointer flex items-center justify-center gap-2.5 text-gray-600 text-sm font-medium hover:border-red-400 hover:text-red-600 hover:bg-red-50 hover:shadow-md transition-all disabled:opacity-50 disabled:cursor-not-allowed", children: isRecording ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: "w-2.5 h-2.5 bg-red-600 rounded-sm animate-pulse" }), "Stop Recording"] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10", strokeWidth: "2" }), jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "4", fill: "currentColor" })] }), "Record Screen"] })) })] })), jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsxs("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: ["Pin Location", " ", !pinPosition && jsxRuntime.jsx("span", { className: "text-red-500", children: "*" })] }), pinPosition ? (jsxRuntime.jsxs("div", { className: "flex items-center gap-3 p-3 bg-green-50 border border-green-200 rounded-xl", children: [jsxRuntime.jsx("div", { className: "w-8 h-8 rounded-full bg-green-500 flex items-center justify-center shadow-md", children: jsxRuntime.jsx("svg", { className: "w-4 h-4 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M5 13l4 4L19 7" }) }) }), jsxRuntime.jsxs("div", { className: "flex-1", children: [jsxRuntime.jsx("div", { className: "text-sm font-medium text-gray-800", children: "Pin placed on page" }), jsxRuntime.jsxs("div", { className: "text-xs text-gray-600", children: ["Position: (", Math.round(pinPosition.pageX), ",", " ", Math.round(pinPosition.pageY), ")"] })] }), jsxRuntime.jsx("div", { onClick: () => setIsPlacingPin(true), className: "px-3 py-1.5 text-xs font-medium text-indigo-600 hover:text-indigo-700 hover:bg-indigo-50 rounded-lg transition-all", children: "Re-place" })] })) : (jsxRuntime.jsxs("div", { onClick: () => setIsPlacingPin(true), className: "p-4 bg-white border-2 border-dashed border-orange-300 rounded-xl cursor-pointer flex items-center justify-center gap-2.5 text-orange-600 text-sm font-medium hover:border-orange-400 hover:bg-orange-50 hover:shadow-md transition-all", children: [jsxRuntime.jsxs("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" }), jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M15 11a3 3 0 11-6 0 3 3 0 016 0z" })] }), "Click to place pin on page"] }))] }), recordedVideo && (jsxRuntime.jsxs("div", { className: "mb-5 relative rounded-xl overflow-hidden shadow-md border border-gray-200", children: [jsxRuntime.jsx("video", { src: recordedVideo, controls: true, className: "w-full h-auto" }), jsxRuntime.jsx("button", { type: "button", onClick: handleDeleteRecording, className: "absolute top-2 right-2 bg-white text-red-500 p-1.5 rounded-lg shadow hover:bg-gray-100 cursor-pointer border-none", children: jsxRuntime.jsx(TrashIcon, { className: "w-3.5 h-3.5" }) })] })), jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Design Mockup" }), designMockup ? (jsxRuntime.jsxs("div", { className: "relative rounded-xl overflow-hidden shadow-md group border border-gray-200", children: [jsxRuntime.jsx("img", { src: designMockup, className: "w-full h-32 object-cover", alt: "Mockup" }), jsxRuntime.jsx("button", { type: "button", onClick: () => setDesignMockup(undefined), className: "absolute top-2 right-2 bg-white text-red-500 p-1.5 rounded-lg shadow hover:bg-gray-100 cursor-pointer border-none", children: jsxRuntime.jsx(TrashIcon, { className: "w-3.5 h-3.5" }) })] })) : (jsxRuntime.jsxs("div", { className: "border-2 border-dashed border-gray-200 rounded-xl p-4 bg-gray-50 flex items-center justify-center relative hover:bg-white hover:border-[#C2D1D9] transition-all", children: [jsxRuntime.jsx("input", { type: "file", accept: "image/*", onChange: handleMockupUpload, className: "absolute inset-0 opacity-0 cursor-pointer w-full h-full" }), jsxRuntime.jsxs("span", { className: "text-sm text-gray-500 flex items-center gap-2 font-medium", children: [jsxRuntime.jsx(PlusIcon, { className: "w-4 h-4" }), " Upload Mockup"] })] }))] }), config.availableTags && config.availableTags.length > 0 && (jsxRuntime.jsxs("div", { className: "mb-5", children: [jsxRuntime.jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Tags" }), jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: config.availableTags.map((tag) => (jsxRuntime.jsx("div", { onClick: () => toggleTag(tag), className: cn("px-3 py-1.5 rounded-full text-xs font-medium cursor-pointer transition-all", selectedTags.includes(tag)
|
|
17618
17684
|
? "bg-[#C2D1D9] text-white shadow-md shadow-[#C2D1D9]/30"
|
|
17619
17685
|
: "bg-gray-100 text-gray-700 hover:bg-gray-200 hover:shadow-sm"), children: tag }, tag))) })] })), jsxRuntime.jsxs("div", { className: "flex gap-3 mt-6 pt-6 border-t border-gray-100", children: [jsxRuntime.jsx("div", { onClick: onCancel, className: "flex-1 py-3 px-5 rounded-xl text-sm font-semibold cursor-pointer transition-all bg-gray-100 text-gray-700 hover:bg-gray-200 hover:shadow-md text-center", children: ((_a = config.labels) === null || _a === void 0 ? void 0 : _a.cancelButton) || "Cancel" }), jsxRuntime.jsx("div", { onClick: (e) => {
|
|
17620
|
-
const isValid = feedbackType === "copy-amendment"
|
|
17686
|
+
const isValid = feedbackType === "copy-amendment"
|
|
17687
|
+
? newCopy.trim()
|
|
17688
|
+
: title.trim();
|
|
17621
17689
|
if (!isValid || isSubmitting)
|
|
17622
17690
|
return;
|
|
17623
17691
|
const form = e.currentTarget.closest("form");
|
|
@@ -17628,12 +17696,18 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17628
17696
|
});
|
|
17629
17697
|
form.dispatchEvent(submitEvent);
|
|
17630
17698
|
}
|
|
17631
|
-
}, className: cn("flex-1 py-3 px-5 rounded-xl text-sm font-semibold cursor-pointer transition-all text-white border-none hover:shadow-lg text-center", ((feedbackType === "copy-amendment"
|
|
17699
|
+
}, className: cn("flex-1 py-3 px-5 rounded-xl text-sm font-semibold cursor-pointer transition-all text-white border-none hover:shadow-lg text-center", ((feedbackType === "copy-amendment"
|
|
17700
|
+
? !newCopy.trim()
|
|
17701
|
+
: !title.trim()) ||
|
|
17702
|
+
isSubmitting) &&
|
|
17632
17703
|
"opacity-50 cursor-not-allowed"), style: {
|
|
17633
|
-
backgroundColor: (feedbackType === "copy-amendment"
|
|
17704
|
+
backgroundColor: (feedbackType === "copy-amendment"
|
|
17705
|
+
? !newCopy.trim()
|
|
17706
|
+
: !title.trim()) || isSubmitting
|
|
17634
17707
|
? "#9ca3af"
|
|
17635
17708
|
: "green-500",
|
|
17636
|
-
}, children: isSubmitting ? (isUploadingVideo ? (jsxRuntime.jsxs("span", { className: "flex items-center justify-center gap-2", children: [jsxRuntime.jsx("span", { className: "w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" }), jsxRuntime.jsx("span", { children: "Uploading video..." })] })) : (jsxRuntime.jsx("span", { className: "w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin mx-auto block" }))) : (((_b = config.labels) === null || _b === void 0 ? void 0 : _b.submitButton) ||
|
|
17709
|
+
}, children: isSubmitting ? (isUploadingVideo ? (jsxRuntime.jsxs("span", { className: "flex items-center justify-center gap-2", children: [jsxRuntime.jsx("span", { className: "w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" }), jsxRuntime.jsx("span", { children: "Uploading video..." })] })) : (jsxRuntime.jsx("span", { className: "w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin mx-auto block" }))) : (((_b = config.labels) === null || _b === void 0 ? void 0 : _b.submitButton) ||
|
|
17710
|
+
(initialData ? "Update Feedback" : "Submit Feedback")) })] })] }));
|
|
17637
17711
|
};
|
|
17638
17712
|
|
|
17639
17713
|
const statusOptions$1 = [
|
|
@@ -39766,6 +39840,7 @@ const subscribeFeedback = (projectId, callback) => {
|
|
|
39766
39840
|
componentName: data.componentName,
|
|
39767
39841
|
designMockup: data.designMockup,
|
|
39768
39842
|
screenRecording: data.screenRecording,
|
|
39843
|
+
pinLocation: data.pinLocation,
|
|
39769
39844
|
};
|
|
39770
39845
|
});
|
|
39771
39846
|
callback(feedbackItems);
|
|
@@ -39874,6 +39949,7 @@ const FeedbackDetail = ({ feedback: initialFeedback, onBack, onDelete, onUpdate,
|
|
|
39874
39949
|
const [showFullScreenshot, setShowFullScreenshot] = require$$0.useState(false);
|
|
39875
39950
|
const [copied, setCopied] = require$$0.useState(false);
|
|
39876
39951
|
const [isEditing, setIsEditing] = require$$0.useState(false);
|
|
39952
|
+
const [showDeleteConfirm, setShowDeleteConfirm] = require$$0.useState(false);
|
|
39877
39953
|
// Feature 2 & 5 states
|
|
39878
39954
|
const [showReplay, setShowReplay] = require$$0.useState(false);
|
|
39879
39955
|
const [showOverlay, setShowOverlay] = require$$0.useState(false);
|
|
@@ -40027,9 +40103,8 @@ ${feedback.comments && feedback.comments.length > 0
|
|
|
40027
40103
|
setNewComment("");
|
|
40028
40104
|
setIsAddingComment(false);
|
|
40029
40105
|
};
|
|
40030
|
-
const handleDelete =
|
|
40031
|
-
|
|
40032
|
-
return;
|
|
40106
|
+
const handleDelete = () => setShowDeleteConfirm(true);
|
|
40107
|
+
const handleConfirmDelete = async () => {
|
|
40033
40108
|
// Delete from Firebase if initialized
|
|
40034
40109
|
try {
|
|
40035
40110
|
await deleteFeedback(feedback.id);
|
|
@@ -40043,8 +40118,10 @@ ${feedback.comments && feedback.comments.length > 0
|
|
|
40043
40118
|
if (onDelete) {
|
|
40044
40119
|
onDelete(feedback.id);
|
|
40045
40120
|
}
|
|
40121
|
+
setShowDeleteConfirm(false);
|
|
40046
40122
|
};
|
|
40047
|
-
|
|
40123
|
+
const handleCancelDelete = () => setShowDeleteConfirm(false);
|
|
40124
|
+
return (jsxRuntime.jsxs("div", { className: "flex flex-col h-full px-5 overflow-y-auto", children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between pb-3 mb-3 border-b border-gray-200", children: [jsxRuntime.jsxs("div", { onClick: onBack, className: "flex items-center gap-1.5 text-sm text-gray-600 bg-transparent border-none cursor-pointer hover:text-gray-900 transition-colors", children: [jsxRuntime.jsx(ArrowLeftIcon, { className: "w-4 h-4" }), "Back"] }), jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsx("div", { onClick: handleCopy, className: "flex items-center gap-1.5 text-sm text-gray-600 bg-transparent border-none cursor-pointer hover:text-gray-900 transition-colors", children: copied ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(CheckIcon, { className: "w-4 h-4" }), "Copied!"] })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx(CopyIcon, { className: "w-4 h-4" }) })) }), canEdit && (jsxRuntime.jsx("div", { onClick: () => setIsEditing(true), className: "flex items-center gap-1.5 text-sm text-gray-600 bg-transparent border-none cursor-pointer hover:text-gray-900 transition-colors", children: jsxRuntime.jsx(EditIcon, { className: "w-4 h-4" }) })), canDelete && onDelete && (jsxRuntime.jsx("div", { onClick: handleDelete, className: "flex items-center gap-1.5 text-sm text-red-600 bg-transparent border-none cursor-pointer hover:text-red-700 transition-colors", children: jsxRuntime.jsx(TrashIcon, { className: "w-4 h-4" }) }))] })] }), showDeleteConfirm && (jsxRuntime.jsxs("div", { className: "mb-3 p-2 rounded-xl border border-red-200 bg-red-50 text-red-800 flex items-center gap-3", children: [jsxRuntime.jsx("div", { className: "flex-1 text-xs font-medium", children: "Delete this feedback?" }), jsxRuntime.jsx("div", { onClick: handleCancelDelete, className: "px-2 py-1.5 text-xs font-semibold text-red-700 rounded-lg hover:bg-red-100 cursor-pointer", children: "Cancel" }), jsxRuntime.jsx("div", { onClick: handleConfirmDelete, className: "px-2 py-1.5 text-xs font-semibold text-white bg-red-600 rounded-lg hover:bg-red-700 cursor-pointer", children: "Delete" })] })), jsxRuntime.jsxs("div", { className: "flex-1 pr-1", children: [jsxRuntime.jsxs("div", { className: "mb-4", children: [jsxRuntime.jsxs("div", { className: "flex items-start gap-2 mb-2", children: [jsxRuntime.jsx("h3", { className: "text-base font-semibold text-gray-900 m-0 flex-1", children: feedback.title }), ((_b = feedback.pageMetadata) === null || _b === void 0 ? void 0 : _b.deviceType) && (jsxRuntime.jsxs("div", { className: "flex-shrink-0 mt-0.5", children: [feedback.pageMetadata.deviceType ===
|
|
40048
40125
|
"desktop" && (jsxRuntime.jsx(DesktopIcon, { className: "w-5 h-5 text-gray-500", title: "Desktop" })), feedback.pageMetadata.deviceType ===
|
|
40049
40126
|
"tablet" && (jsxRuntime.jsx(TabletIcon, { className: "w-5 h-5 text-gray-500", title: "Tablet" })), feedback.pageMetadata.deviceType ===
|
|
40050
40127
|
"mobile" && (jsxRuntime.jsx(MobileIcon, { className: "w-5 h-5 text-gray-500", title: "Mobile" }))] }))] }), jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [feedback.type && (jsxRuntime.jsx("span", { className: cn("px-2 py-0.5 rounded-full text-[10px] font-medium uppercase", feedback.type === "copy-amendment"
|
|
@@ -40225,6 +40302,89 @@ const ViewportControls = () => {
|
|
|
40225
40302
|
: "hover:bg-gray-100"), children: [jsxRuntime.jsx(DesktopIcon, { className: cn("w-4 h-4 transition-colors", isActivePreset(preset) ? "text-blue-600" : "text-gray-400 group-hover:text-gray-600") }), jsxRuntime.jsxs("div", { className: "flex-1", children: [jsxRuntime.jsx("div", { className: "text-sm font-medium text-gray-700 group-hover:text-gray-900", children: preset.name }), jsxRuntime.jsxs("div", { className: "text-xs text-gray-500", children: [preset.width, " \u00D7 ", preset.height] })] })] }, preset.name)))] }) }))] }));
|
|
40226
40303
|
};
|
|
40227
40304
|
|
|
40305
|
+
const FeedbackPins = ({ onPinClick }) => {
|
|
40306
|
+
const { feedbackItems, showPins } = useMarkupStore();
|
|
40307
|
+
const [docSize, setDocSize] = require$$0.useState({ width: 0, height: 0 });
|
|
40308
|
+
require$$0.useEffect(() => {
|
|
40309
|
+
const measure = () => {
|
|
40310
|
+
const root = document.documentElement;
|
|
40311
|
+
const body = document.body;
|
|
40312
|
+
const width = root.clientWidth || body.clientWidth || window.innerWidth;
|
|
40313
|
+
const height = Math.max(root.scrollHeight, body.scrollHeight, root.clientHeight, body.clientHeight);
|
|
40314
|
+
setDocSize({ width, height });
|
|
40315
|
+
};
|
|
40316
|
+
measure();
|
|
40317
|
+
const resizeObserver = new ResizeObserver(() => measure());
|
|
40318
|
+
resizeObserver.observe(document.documentElement);
|
|
40319
|
+
resizeObserver.observe(document.body);
|
|
40320
|
+
const handleResize = () => measure();
|
|
40321
|
+
const handleScroll = () => measure();
|
|
40322
|
+
window.addEventListener('resize', handleResize);
|
|
40323
|
+
window.addEventListener('scroll', handleScroll, { passive: true });
|
|
40324
|
+
return () => {
|
|
40325
|
+
resizeObserver.disconnect();
|
|
40326
|
+
window.removeEventListener('resize', handleResize);
|
|
40327
|
+
window.removeEventListener('scroll', handleScroll);
|
|
40328
|
+
};
|
|
40329
|
+
}, []);
|
|
40330
|
+
// Filter pins for current page URL
|
|
40331
|
+
const currentPagePins = require$$0.useMemo(() => {
|
|
40332
|
+
const currentUrl = window.location.href;
|
|
40333
|
+
return feedbackItems.filter(item => {
|
|
40334
|
+
var _a;
|
|
40335
|
+
// Only show pins with pinLocation data
|
|
40336
|
+
if (!item.pinLocation)
|
|
40337
|
+
return false;
|
|
40338
|
+
// Only show pins from the same page
|
|
40339
|
+
if (((_a = item.pageMetadata) === null || _a === void 0 ? void 0 : _a.url) !== currentUrl)
|
|
40340
|
+
return false;
|
|
40341
|
+
return true;
|
|
40342
|
+
});
|
|
40343
|
+
}, [feedbackItems]);
|
|
40344
|
+
// Don't show pins if toggle is off
|
|
40345
|
+
if (!showPins)
|
|
40346
|
+
return null;
|
|
40347
|
+
if (currentPagePins.length === 0)
|
|
40348
|
+
return null;
|
|
40349
|
+
const containerStyle = {
|
|
40350
|
+
position: 'absolute',
|
|
40351
|
+
top: 0,
|
|
40352
|
+
left: 0,
|
|
40353
|
+
width: docSize.width || '100%',
|
|
40354
|
+
height: docSize.height || '100%',
|
|
40355
|
+
pointerEvents: 'none',
|
|
40356
|
+
zIndex: 999998,
|
|
40357
|
+
};
|
|
40358
|
+
return (jsxRuntime.jsx("div", { className: "feedback-pins-container", "data-markup-pins": true, style: containerStyle, children: currentPagePins.map((feedback) => {
|
|
40359
|
+
const { pinLocation } = feedback;
|
|
40360
|
+
if (!pinLocation)
|
|
40361
|
+
return null;
|
|
40362
|
+
const pageWidth = docSize.width || pinLocation.pageWidth || pinLocation.viewportWidth || window.innerWidth;
|
|
40363
|
+
const pageHeight = docSize.height || pinLocation.pageHeight || pinLocation.viewportHeight || window.innerHeight;
|
|
40364
|
+
// Prefer stored percentages; fall back to computing from stored absolute coords
|
|
40365
|
+
const leftPercent = typeof pinLocation.percentX === 'number'
|
|
40366
|
+
? pinLocation.percentX
|
|
40367
|
+
: pageWidth ? (pinLocation.pageX / pageWidth) * 100 : 0;
|
|
40368
|
+
const topPercent = typeof pinLocation.percentY === 'number'
|
|
40369
|
+
? pinLocation.percentY
|
|
40370
|
+
: pageHeight ? (pinLocation.pageY / pageHeight) * 100 : 0;
|
|
40371
|
+
const style = {
|
|
40372
|
+
position: 'absolute',
|
|
40373
|
+
left: `${leftPercent}%`,
|
|
40374
|
+
top: `${topPercent}%`,
|
|
40375
|
+
transform: 'translate(-50%, -50%)',
|
|
40376
|
+
pointerEvents: 'auto',
|
|
40377
|
+
};
|
|
40378
|
+
return (jsxRuntime.jsx("div", { className: "feedback-pin-wrapper", style: style, onClick: () => onPinClick(feedback), children: jsxRuntime.jsxs("div", { className: "feedback-pin group relative cursor-pointer", children: [jsxRuntime.jsx("div", { className: `
|
|
40379
|
+
w-8 h-8 rounded-full flex items-center justify-center
|
|
40380
|
+
shadow-lg hover:shadow-xl transition-all
|
|
40381
|
+
${feedback.status === 'open' ? 'bg-[#E6B6CF] hover:bg-[#d9a3c0]' : 'bg-green-500 hover:bg-green-600'}
|
|
40382
|
+
border-2 border-white
|
|
40383
|
+
hover:scale-110
|
|
40384
|
+
`, children: jsxRuntime.jsx(MessageIcon, { className: "w-4 h-4 text-white" }) }), jsxRuntime.jsxs("div", { className: "\n absolute bottom-full left-1/2 -translate-x-1/2 mb-2\n opacity-0 group-hover:opacity-100\n pointer-events-none\n transition-opacity duration-200\n bg-gray-900 text-white text-xs rounded-lg py-2 px-3\n whitespace-nowrap shadow-xl\n max-w-[200px]\n ", children: [jsxRuntime.jsx("div", { className: "font-semibold truncate", children: feedback.title || 'Feedback' }), jsxRuntime.jsx("div", { className: "text-gray-300 text-[10px] mt-0.5", children: "Click to view" }), jsxRuntime.jsx("div", { className: "\n absolute top-full left-1/2 -translate-x-1/2\n w-0 h-0\n border-l-4 border-l-transparent\n border-r-4 border-r-transparent\n border-t-4 border-t-gray-900\n " })] })] }) }, feedback.id));
|
|
40385
|
+
}) }));
|
|
40386
|
+
};
|
|
40387
|
+
|
|
40228
40388
|
const useFirebaseSync = () => {
|
|
40229
40389
|
const config = useMarkupStore((state) => state.config);
|
|
40230
40390
|
const setCurrentUser = useMarkupStore((state) => state.setCurrentUser);
|
|
@@ -40436,7 +40596,7 @@ const MarkupWidget = ({ config: userConfig, }) => {
|
|
|
40436
40596
|
console.error('Failed to initialize Firebase:', error);
|
|
40437
40597
|
}
|
|
40438
40598
|
}
|
|
40439
|
-
const { isOpen, setIsOpen, activeTab, setActiveTab, selectedFeedback, setSelectedFeedback, feedbackItems, addFeedbackItem, removeFeedbackItem, updateFeedback, config, setConfig, reset, isAuthenticated, currentUser, viewportMode, setViewportMode, } = useMarkupStore();
|
|
40599
|
+
const { isOpen, setIsOpen, activeTab, setActiveTab, selectedFeedback, setSelectedFeedback, feedbackItems, addFeedbackItem, removeFeedbackItem, updateFeedback, config, setConfig, reset, isAuthenticated, currentUser, viewportMode, setViewportMode, showPins, setShowPins, } = useMarkupStore();
|
|
40440
40600
|
// Initialize Firebase sync
|
|
40441
40601
|
useFirebaseSync();
|
|
40442
40602
|
// Initialize Session Recording (Feature 2)
|
|
@@ -40575,6 +40735,11 @@ const MarkupWidget = ({ config: userConfig, }) => {
|
|
|
40575
40735
|
const handleSelectFeedback = require$$0.useCallback((feedback) => {
|
|
40576
40736
|
setSelectedFeedback(feedback);
|
|
40577
40737
|
}, [setSelectedFeedback]);
|
|
40738
|
+
const handlePinClick = require$$0.useCallback((feedback) => {
|
|
40739
|
+
setIsOpen(true);
|
|
40740
|
+
setActiveTab("list");
|
|
40741
|
+
setSelectedFeedback(feedback);
|
|
40742
|
+
}, [setIsOpen, setActiveTab, setSelectedFeedback]);
|
|
40578
40743
|
const handleBackFromDetail = require$$0.useCallback(() => {
|
|
40579
40744
|
setSelectedFeedback(null);
|
|
40580
40745
|
setViewportMode(null);
|
|
@@ -40618,11 +40783,14 @@ const MarkupWidget = ({ config: userConfig, }) => {
|
|
|
40618
40783
|
? adjustColor(config.primaryColor, -20)
|
|
40619
40784
|
: "#4f46e5",
|
|
40620
40785
|
});
|
|
40621
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ViewportControls, {}), jsxRuntime.jsxs("div", { className: cn("markup-widget fixed z-[999999] right-0 top-1/2 flex items-center transition-transform duration-300 ease-in-out", isOpen ? "" : "hover:translate-x-[-8px]"), style: {
|
|
40786
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(FeedbackPins, { onPinClick: handlePinClick }), jsxRuntime.jsx(ViewportControls, {}), jsxRuntime.jsxs("div", { className: cn("markup-widget fixed z-[999999] right-0 top-1/2 flex items-center transition-transform duration-300 ease-in-out", isOpen ? "" : "hover:translate-x-[-8px]"), style: {
|
|
40622
40787
|
transform: isOpen
|
|
40623
40788
|
? "translateY(-50%) translateX(0)"
|
|
40624
40789
|
: "translateY(-50%) translateX(calc(100% - 40px))",
|
|
40625
|
-
}, "data-markup-widget": true, children: [jsxRuntime.jsxs("div", { onClick: handleToggle, className: "relative bg-white h-fit flex items-center gap-2 px-2 pr-5 text-black border-none rounded-l-2xl cursor-pointer text-sm font-semibold transition-all py-5 shadow-lg", children: [isOpen && jsxRuntime.jsx(CloseIcon, { className: "w-5 h-5" }), jsxRuntime.jsx("span", { className: "text-black", style: { writingMode: "sideways-lr" }, children: "Siren" }), openFeedbackCount > 0 && (jsxRuntime.jsx("span", { className: "absolute -top-3 -left-1 min-w-[24px] h-[24px] px-1 bg-[#E6B6CF] text-black rounded-full text-xs font-semibold flex items-center justify-center", children: openFeedbackCount }))] }), jsxRuntime.jsxs("div", { className: "bg-white w-[500px] h-[750px] rounded-l-xl shadow-2xl overflow-hidden flex flex-col text-black", children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-5 py-4 text-black", style: { backgroundColor: "var(--markup-primary)" }, children: [jsxRuntime.jsx("h2", { className: "text-base font-semibold m-0", children: ((_a = config.labels) === null || _a === void 0 ? void 0 : _a.feedbackTitle) || "Feedback" }),
|
|
40790
|
+
}, "data-markup-widget": true, children: [jsxRuntime.jsxs("div", { onClick: handleToggle, className: "relative bg-white h-fit flex items-center gap-2 px-2 pr-5 text-black border-none rounded-l-2xl cursor-pointer text-sm font-semibold transition-all py-5 shadow-lg", children: [isOpen && jsxRuntime.jsx(CloseIcon, { className: "w-5 h-5" }), jsxRuntime.jsx("span", { className: "text-black", style: { writingMode: "sideways-lr" }, children: "Siren" }), openFeedbackCount > 0 && (jsxRuntime.jsx("span", { className: "absolute -top-3 -left-1 min-w-[24px] h-[24px] px-1 bg-[#E6B6CF] text-black rounded-full text-xs font-semibold flex items-center justify-center", children: openFeedbackCount }))] }), jsxRuntime.jsxs("div", { className: "bg-white w-[500px] h-[750px] rounded-l-xl shadow-2xl overflow-hidden flex flex-col text-black", children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-5 py-4 text-black", style: { backgroundColor: "var(--markup-primary)" }, children: [jsxRuntime.jsx("h2", { className: "text-base font-semibold m-0", children: ((_a = config.labels) === null || _a === void 0 ? void 0 : _a.feedbackTitle) || "Feedback" }), jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsxs("div", { onClick: (e) => {
|
|
40791
|
+
e.stopPropagation();
|
|
40792
|
+
setShowPins(!showPins);
|
|
40793
|
+
}, className: "p-2 text-xs font-medium bg-gray-50 border border-gray-200 text-black rounded-lg hover:bg-gray-100 cursor-pointer transition-all flex items-center gap-1.5", title: showPins ? "Hide pins" : "Show pins", children: [jsxRuntime.jsxs("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" }), jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M15 11a3 3 0 11-6 0 3 3 0 016 0z" })] }), showPins ? 'Hide' : 'Show'] }), isAuthenticated && currentUser && (jsxRuntime.jsx("div", { onClick: handleLogout, className: "text-xs py-2 px-2 bg-gray-50 border border-gray-200 text-gray-900 rounded-lg hover:bg-gray-200 cursor-pointer transition-all flex items-center gap-2", children: jsxRuntime.jsx("h2", { children: "Sign Out" }) }))] })] }), !isAuthenticated ? (jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto", children: jsxRuntime.jsx(AuthForm, { onSuccess: handleAuthSuccess }) })) : selectedFeedback ? (jsxRuntime.jsx(FeedbackDetail, { feedback: selectedFeedback, onBack: handleBackFromDetail, onDelete: handleDeleteFeedback, onUpdate: handleUpdateFeedback })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "flex border-b border-gray-200 px-5", children: [jsxRuntime.jsxs("div", { className: cn("flex-1 py-3 px-4 border border-b-0 border-gray-200 rounded-t-xl hover:bg-gray-100 cursor-pointer text-sm font-medium transition-all flex items-center justify-center gap-1", activeTab === "create"
|
|
40626
40794
|
? "bg-gray-200"
|
|
40627
40795
|
: "border-white"), onClick: () => setActiveTab("create"), children: [jsxRuntime.jsx(PlusIcon, { className: "w-3.5 h-3.5" }), "New"] }), jsxRuntime.jsxs("div", { className: cn("flex-1 py-3 px-4 border border-b-0 border-gray-200 rounded-t-xl hover:bg-gray-100 cursor-pointer text-sm font-medium transition-all flex items-center justify-center gap-1", activeTab === "list"
|
|
40628
40796
|
? "bg-gray-200"
|