@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.esm.js
CHANGED
|
@@ -847,6 +847,7 @@ const initialState = {
|
|
|
847
847
|
currentUser: null,
|
|
848
848
|
isAuthenticated: false,
|
|
849
849
|
viewportMode: null,
|
|
850
|
+
showPins: true,
|
|
850
851
|
};
|
|
851
852
|
const useMarkupStore = create()(persist((set, get) => ({
|
|
852
853
|
...initialState,
|
|
@@ -909,6 +910,7 @@ const useMarkupStore = create()(persist((set, get) => ({
|
|
|
909
910
|
activeTab: 'create',
|
|
910
911
|
activeTool: null,
|
|
911
912
|
}),
|
|
913
|
+
setShowPins: (show) => set({ showPins: show }),
|
|
912
914
|
}), {
|
|
913
915
|
name: 'markup-storage',
|
|
914
916
|
storage: createJSONStorage(() => localStorage),
|
|
@@ -17030,6 +17032,7 @@ const deleteScreenRecording = async (downloadURL) => {
|
|
|
17030
17032
|
}
|
|
17031
17033
|
};
|
|
17032
17034
|
|
|
17035
|
+
const MessageIcon = ({ className, ...props }) => (jsx("svg", { className: className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: 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" }) }));
|
|
17033
17036
|
const CloseIcon = ({ className, ...props }) => (jsxs("svg", { className: className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }));
|
|
17034
17037
|
const CameraIcon = ({ className, ...props }) => (jsxs("svg", { className: className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [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" }), jsx("circle", { cx: "12", cy: "13", r: "4" })] }));
|
|
17035
17038
|
const CircleIcon = ({ className, ...props }) => (jsx("svg", { className: className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: jsx("circle", { cx: "12", cy: "12", r: "10" }) }));
|
|
@@ -17372,7 +17375,7 @@ const AnnotationOverlay = ({ screenshot, onComplete, onCancel, }) => {
|
|
|
17372
17375
|
|
|
17373
17376
|
const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastClickedElement, lastClickedText, }) => {
|
|
17374
17377
|
var _a, _b;
|
|
17375
|
-
const { config, currentScreenshot, setCurrentScreenshot, annotations, setAnnotations, clearAnnotations, setIsCapturing, isCapturing, currentUser, } = useMarkupStore();
|
|
17378
|
+
const { config, currentScreenshot, setCurrentScreenshot, annotations, setAnnotations, clearAnnotations, setIsCapturing, isCapturing, currentUser, setIsOpen, } = useMarkupStore();
|
|
17376
17379
|
const [title, setTitle] = useState((initialData === null || initialData === void 0 ? void 0 : initialData.title) || "");
|
|
17377
17380
|
const [description, setDescription] = useState((initialData === null || initialData === void 0 ? void 0 : initialData.description) || "");
|
|
17378
17381
|
const [priority, setPriority] = useState((initialData === null || initialData === void 0 ? void 0 : initialData.priority) || "medium");
|
|
@@ -17391,12 +17394,48 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17391
17394
|
const [isUploadingVideo, setIsUploadingVideo] = useState(false);
|
|
17392
17395
|
const [mediaRecorder, setMediaRecorder] = useState(null);
|
|
17393
17396
|
const [recordingStream, setRecordingStream] = useState(null);
|
|
17397
|
+
// Pin placement states
|
|
17398
|
+
const [isPlacingPin, setIsPlacingPin] = useState(false);
|
|
17399
|
+
const [pinPosition, setPinPosition] = useState((initialData === null || initialData === void 0 ? void 0 : initialData.pinLocation)
|
|
17400
|
+
? {
|
|
17401
|
+
x: initialData.pinLocation.x,
|
|
17402
|
+
y: initialData.pinLocation.y,
|
|
17403
|
+
pageX: initialData.pinLocation.pageX,
|
|
17404
|
+
pageY: initialData.pinLocation.pageY,
|
|
17405
|
+
}
|
|
17406
|
+
: null);
|
|
17394
17407
|
// Load initial annotations into store if editing
|
|
17395
17408
|
useEffect(() => {
|
|
17396
17409
|
if (initialData === null || initialData === void 0 ? void 0 : initialData.annotations) {
|
|
17397
17410
|
setAnnotations(initialData.annotations);
|
|
17398
17411
|
}
|
|
17399
17412
|
}, [initialData, setAnnotations]);
|
|
17413
|
+
// Handle pin placement mode
|
|
17414
|
+
useEffect(() => {
|
|
17415
|
+
if (!isPlacingPin)
|
|
17416
|
+
return;
|
|
17417
|
+
// Close widget during pin placement
|
|
17418
|
+
setIsOpen(false);
|
|
17419
|
+
const handlePinClick = (e) => {
|
|
17420
|
+
e.preventDefault();
|
|
17421
|
+
e.stopPropagation();
|
|
17422
|
+
setPinPosition({
|
|
17423
|
+
x: e.clientX,
|
|
17424
|
+
y: e.clientY,
|
|
17425
|
+
pageX: e.pageX,
|
|
17426
|
+
pageY: e.pageY,
|
|
17427
|
+
});
|
|
17428
|
+
setIsPlacingPin(false);
|
|
17429
|
+
};
|
|
17430
|
+
document.addEventListener('click', handlePinClick, true);
|
|
17431
|
+
document.body.style.cursor = 'crosshair';
|
|
17432
|
+
return () => {
|
|
17433
|
+
document.removeEventListener('click', handlePinClick, true);
|
|
17434
|
+
document.body.style.cursor = '';
|
|
17435
|
+
// Reopen widget when pin placement is done
|
|
17436
|
+
setIsOpen(true);
|
|
17437
|
+
};
|
|
17438
|
+
}, [isPlacingPin, setIsOpen]);
|
|
17400
17439
|
// Auto-capture selected text when component mounts (only if creating new)
|
|
17401
17440
|
useEffect(() => {
|
|
17402
17441
|
var _a;
|
|
@@ -17434,12 +17473,12 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17434
17473
|
const handleStartRecording = async () => {
|
|
17435
17474
|
try {
|
|
17436
17475
|
const stream = await navigator.mediaDevices.getDisplayMedia({
|
|
17437
|
-
video: { mediaSource:
|
|
17476
|
+
video: { mediaSource: "screen" },
|
|
17438
17477
|
audio: false,
|
|
17439
17478
|
});
|
|
17440
17479
|
setRecordingStream(stream);
|
|
17441
17480
|
const recorder = new MediaRecorder(stream, {
|
|
17442
|
-
mimeType:
|
|
17481
|
+
mimeType: "video/webm;codecs=vp8",
|
|
17443
17482
|
});
|
|
17444
17483
|
const chunks = [];
|
|
17445
17484
|
recorder.ondataavailable = (e) => {
|
|
@@ -17448,14 +17487,14 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17448
17487
|
}
|
|
17449
17488
|
};
|
|
17450
17489
|
recorder.onstop = () => {
|
|
17451
|
-
const blob = new Blob(chunks, { type:
|
|
17490
|
+
const blob = new Blob(chunks, { type: "video/webm" });
|
|
17452
17491
|
// Store blob for upload and create preview URL
|
|
17453
17492
|
setRecordedBlob(blob);
|
|
17454
17493
|
const previewURL = URL.createObjectURL(blob);
|
|
17455
17494
|
setRecordedVideo(previewURL);
|
|
17456
17495
|
console.log(`Recording saved: ${(blob.size / 1048576).toFixed(2)}MB`);
|
|
17457
17496
|
// Stop all tracks
|
|
17458
|
-
stream.getTracks().forEach(track => track.stop());
|
|
17497
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
17459
17498
|
setRecordingStream(null);
|
|
17460
17499
|
};
|
|
17461
17500
|
recorder.start();
|
|
@@ -17463,25 +17502,25 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17463
17502
|
setIsRecording(true);
|
|
17464
17503
|
}
|
|
17465
17504
|
catch (error) {
|
|
17466
|
-
console.error(
|
|
17467
|
-
alert(
|
|
17505
|
+
console.error("Failed to start recording:", error);
|
|
17506
|
+
alert("Screen recording is not supported or permission was denied.");
|
|
17468
17507
|
}
|
|
17469
17508
|
};
|
|
17470
17509
|
const handleStopRecording = () => {
|
|
17471
|
-
if (mediaRecorder && mediaRecorder.state !==
|
|
17510
|
+
if (mediaRecorder && mediaRecorder.state !== "inactive") {
|
|
17472
17511
|
mediaRecorder.stop();
|
|
17473
17512
|
setIsRecording(false);
|
|
17474
17513
|
}
|
|
17475
17514
|
};
|
|
17476
17515
|
const handleDeleteRecording = () => {
|
|
17477
17516
|
// Revoke blob URL to free memory
|
|
17478
|
-
if (recordedVideo && recordedVideo.startsWith(
|
|
17517
|
+
if (recordedVideo && recordedVideo.startsWith("blob:")) {
|
|
17479
17518
|
URL.revokeObjectURL(recordedVideo);
|
|
17480
17519
|
}
|
|
17481
17520
|
setRecordedVideo(undefined);
|
|
17482
17521
|
setRecordedBlob(null);
|
|
17483
17522
|
if (recordingStream) {
|
|
17484
|
-
recordingStream.getTracks().forEach(track => track.stop());
|
|
17523
|
+
recordingStream.getTracks().forEach((track) => track.stop());
|
|
17485
17524
|
setRecordingStream(null);
|
|
17486
17525
|
}
|
|
17487
17526
|
};
|
|
@@ -17539,11 +17578,11 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17539
17578
|
setIsUploadingVideo(true);
|
|
17540
17579
|
const tempId = generateId();
|
|
17541
17580
|
screenRecordingURL = await uploadScreenRecording(recordedBlob, tempId);
|
|
17542
|
-
console.log(
|
|
17581
|
+
console.log("Video uploaded to Storage:", screenRecordingURL);
|
|
17543
17582
|
}
|
|
17544
17583
|
catch (error) {
|
|
17545
|
-
console.error(
|
|
17546
|
-
alert(
|
|
17584
|
+
console.error("Failed to upload video:", error);
|
|
17585
|
+
alert("Failed to upload screen recording. Please try again.");
|
|
17547
17586
|
setIsSubmitting(false);
|
|
17548
17587
|
setIsUploadingVideo(false);
|
|
17549
17588
|
return;
|
|
@@ -17552,10 +17591,13 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17552
17591
|
setIsUploadingVideo(false);
|
|
17553
17592
|
}
|
|
17554
17593
|
}
|
|
17594
|
+
const pageWidth = document.documentElement.scrollWidth;
|
|
17595
|
+
const pageHeight = document.documentElement.scrollHeight;
|
|
17555
17596
|
const feedback = {
|
|
17556
17597
|
id: (initialData === null || initialData === void 0 ? void 0 : initialData.id) || generateId(),
|
|
17557
17598
|
type: feedbackType,
|
|
17558
|
-
title: title.trim() ||
|
|
17599
|
+
title: title.trim() ||
|
|
17600
|
+
(feedbackType === "copy-amendment" ? "Copy Amendment" : ""),
|
|
17559
17601
|
description: description.trim(),
|
|
17560
17602
|
currentCopy: feedbackType === "copy-amendment"
|
|
17561
17603
|
? currentCopy.trim()
|
|
@@ -17565,10 +17607,12 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17565
17607
|
annotations: annotations,
|
|
17566
17608
|
status: (initialData === null || initialData === void 0 ? void 0 : initialData.status) || "open",
|
|
17567
17609
|
priority,
|
|
17568
|
-
pageMetadata: (initialData === null || initialData === void 0 ? void 0 : initialData.pageMetadata) ||
|
|
17610
|
+
pageMetadata: (initialData === null || initialData === void 0 ? void 0 : initialData.pageMetadata) ||
|
|
17611
|
+
getPageMetadata(lastClickedElement || undefined),
|
|
17569
17612
|
createdAt: (initialData === null || initialData === void 0 ? void 0 : initialData.createdAt) || new Date().toISOString(),
|
|
17570
17613
|
updatedAt: new Date().toISOString(),
|
|
17571
|
-
createdBy: (initialData === null || initialData === void 0 ? void 0 : initialData.createdBy) ||
|
|
17614
|
+
createdBy: (initialData === null || initialData === void 0 ? void 0 : initialData.createdBy) ||
|
|
17615
|
+
currentUser || {
|
|
17572
17616
|
id: ((_a = config.user) === null || _a === void 0 ? void 0 : _a.id) || "anonymous",
|
|
17573
17617
|
name: ((_b = config.user) === null || _b === void 0 ? void 0 : _b.name) || "Anonymous",
|
|
17574
17618
|
email: ((_c = config.user) === null || _c === void 0 ? void 0 : _c.email) || "",
|
|
@@ -17579,6 +17623,22 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17579
17623
|
sessionEvents: getSessionEvents ? getSessionEvents() : undefined,
|
|
17580
17624
|
designMockup,
|
|
17581
17625
|
screenRecording: screenRecordingURL,
|
|
17626
|
+
pinLocation: pinPosition
|
|
17627
|
+
? {
|
|
17628
|
+
x: pinPosition.x,
|
|
17629
|
+
y: pinPosition.y,
|
|
17630
|
+
pageX: pinPosition.pageX,
|
|
17631
|
+
pageY: pinPosition.pageY,
|
|
17632
|
+
viewportWidth: window.innerWidth,
|
|
17633
|
+
viewportHeight: window.innerHeight,
|
|
17634
|
+
pageWidth,
|
|
17635
|
+
pageHeight,
|
|
17636
|
+
scrollX: window.scrollX,
|
|
17637
|
+
scrollY: window.scrollY,
|
|
17638
|
+
percentX: pageWidth ? (pinPosition.pageX / pageWidth) * 100 : undefined,
|
|
17639
|
+
percentY: pageHeight ? (pinPosition.pageY / pageHeight) * 100 : undefined,
|
|
17640
|
+
}
|
|
17641
|
+
: undefined,
|
|
17582
17642
|
};
|
|
17583
17643
|
try {
|
|
17584
17644
|
if (config.onSubmit) {
|
|
@@ -17603,6 +17663,10 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17603
17663
|
if (isAnnotating && currentScreenshot) {
|
|
17604
17664
|
return (jsx(AnnotationOverlay, { screenshot: currentScreenshot, onComplete: handleAnnotationComplete, onCancel: handleAnnotationCancel }));
|
|
17605
17665
|
}
|
|
17666
|
+
// Show pin placement overlay
|
|
17667
|
+
if (isPlacingPin) {
|
|
17668
|
+
return (jsx("div", { className: "fixed top-4 left-1/2 -translate-x-1/2 z-[999999] pointer-events-none", children: jsxs("div", { className: "bg-white rounded-2xl shadow-2xl p-6 max-w-md mx-4 text-center border-2 border-orange-500", children: [jsx("div", { className: "w-12 h-12 mx-auto mb-3 bg-orange-100 rounded-full flex items-center justify-center", children: jsxs("svg", { className: "w-6 h-6 text-orange-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [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" }), jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M15 11a3 3 0 11-6 0 3 3 0 016 0z" })] }) }), jsx("h3", { className: "text-lg font-bold text-gray-900 mb-2", children: "Click to Place Pin" }), jsx("p", { className: "text-gray-600 text-sm mb-3", children: "Click anywhere on the page to mark the location" }), 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" })] }) }));
|
|
17669
|
+
}
|
|
17606
17670
|
return (jsxs("form", { onSubmit: handleSubmit, children: [jsxs("div", { className: "mb-5", children: [jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Type" }), jsx("div", { className: "flex gap-2", children: [
|
|
17607
17671
|
{ id: "general", label: "General" },
|
|
17608
17672
|
{ id: "copy-amendment", label: "Copy Change" },
|
|
@@ -17610,10 +17674,14 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17610
17674
|
? "bg-[#C2D1D9] text-black shadow-md shadow-[#C2D1D9]/30"
|
|
17611
17675
|
: "bg-gray-100 text-gray-700 hover:bg-gray-200 hover:shadow-sm"), children: type.label }, type.id))) })] }), feedbackType === "copy-amendment" && (jsxs(Fragment, { children: [jsxs("div", { className: "mb-5", children: [jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Current Copy" }), 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" })] }), jsxs("div", { className: "mb-5", children: [jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "New Copy *" }), 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" && (jsxs("div", { className: "mb-5", children: [jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Title *" }), 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" && (jsxs("div", { className: "mb-5", children: [jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Description" }), 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" })] })), jsxs("div", { className: "mb-5", children: [jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Priority" }), jsx("div", { className: "flex gap-2", children: Object.keys(priorityColors).map((p) => (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
|
|
17612
17676
|
? cn(priorityColors[p], "border-transparent shadow-sm")
|
|
17613
|
-
: "bg-gray-100 text-gray-600 hover:bg-gray-200"), children: p }, p))) })] }), annotatedScreenshot || currentScreenshot ? (jsxs("div", { className: "relative rounded-xl overflow-hidden shadow-md mb-5 group", children: [jsx("img", { src: annotatedScreenshot || currentScreenshot || "", alt: "Screenshot", className: "w-full block" }), jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/40 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" }), jsxs("div", { className: "absolute top-3 right-3 flex gap-2", children: [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: [jsx(EditIcon, { className: "w-3.5 h-3.5" }), " Edit"] }), 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: [jsx(TrashIcon, { className: "w-3.5 h-3.5" }), " Remove"] })] })] })) : (jsxs("div", { className: "flex gap-3 mb-5", children: [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 ? (jsxs(Fragment, { children: [jsx("span", { className: "w-5 h-5 border-2 border-indigo-200 border-t-indigo-600 rounded-full animate-spin" }), "Capturing..."] })) : (jsxs(Fragment, { children: [jsx(CameraIcon, { className: "w-5 h-5" }), " Screenshot"] })) }), jsx("div", { onClick: isRecording
|
|
17677
|
+
: "bg-gray-100 text-gray-600 hover:bg-gray-200"), children: p }, p))) })] }), annotatedScreenshot || currentScreenshot ? (jsxs("div", { className: "relative rounded-xl overflow-hidden shadow-md mb-5 group", children: [jsx("img", { src: annotatedScreenshot || currentScreenshot || "", alt: "Screenshot", className: "w-full block" }), jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/40 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" }), jsxs("div", { className: "absolute top-3 right-3 flex gap-2", children: [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: [jsx(EditIcon, { className: "w-3.5 h-3.5" }), " Edit"] }), 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: [jsx(TrashIcon, { className: "w-3.5 h-3.5" }), " Remove"] })] })] })) : (jsxs("div", { className: "flex gap-3 mb-5", children: [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 ? (jsxs(Fragment, { children: [jsx("span", { className: "w-5 h-5 border-2 border-indigo-200 border-t-indigo-600 rounded-full animate-spin" }), "Capturing..."] })) : (jsxs(Fragment, { children: [jsx(CameraIcon, { className: "w-5 h-5" }), " Screenshot"] })) }), jsx("div", { onClick: isRecording
|
|
17678
|
+
? handleStopRecording
|
|
17679
|
+
: 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 ? (jsxs(Fragment, { children: [jsx("span", { className: "w-2.5 h-2.5 bg-red-600 rounded-sm animate-pulse" }), "Stop Recording"] })) : (jsxs(Fragment, { children: [jsxs("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [jsx("circle", { cx: "12", cy: "12", r: "10", strokeWidth: "2" }), jsx("circle", { cx: "12", cy: "12", r: "4", fill: "currentColor" })] }), "Record Screen"] })) })] })), jsxs("div", { className: "mb-5", children: [jsxs("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: ["Pin Location", " ", !pinPosition && jsx("span", { className: "text-red-500", children: "*" })] }), pinPosition ? (jsxs("div", { className: "flex items-center gap-3 p-3 bg-green-50 border border-green-200 rounded-xl", children: [jsx("div", { className: "w-8 h-8 rounded-full bg-green-500 flex items-center justify-center shadow-md", children: jsx("svg", { className: "w-4 h-4 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M5 13l4 4L19 7" }) }) }), jsxs("div", { className: "flex-1", children: [jsx("div", { className: "text-sm font-medium text-gray-800", children: "Pin placed on page" }), jsxs("div", { className: "text-xs text-gray-600", children: ["Position: (", Math.round(pinPosition.pageX), ",", " ", Math.round(pinPosition.pageY), ")"] })] }), 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" })] })) : (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: [jsxs("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [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" }), 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 && (jsxs("div", { className: "mb-5 relative rounded-xl overflow-hidden shadow-md border border-gray-200", children: [jsx("video", { src: recordedVideo, controls: true, className: "w-full h-auto" }), 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: jsx(TrashIcon, { className: "w-3.5 h-3.5" }) })] })), jsxs("div", { className: "mb-5", children: [jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Design Mockup" }), designMockup ? (jsxs("div", { className: "relative rounded-xl overflow-hidden shadow-md group border border-gray-200", children: [jsx("img", { src: designMockup, className: "w-full h-32 object-cover", alt: "Mockup" }), 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: jsx(TrashIcon, { className: "w-3.5 h-3.5" }) })] })) : (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: [jsx("input", { type: "file", accept: "image/*", onChange: handleMockupUpload, className: "absolute inset-0 opacity-0 cursor-pointer w-full h-full" }), jsxs("span", { className: "text-sm text-gray-500 flex items-center gap-2 font-medium", children: [jsx(PlusIcon, { className: "w-4 h-4" }), " Upload Mockup"] })] }))] }), config.availableTags && config.availableTags.length > 0 && (jsxs("div", { className: "mb-5", children: [jsx("label", { className: "block text-sm font-semibold mb-2 text-gray-800", children: "Tags" }), jsx("div", { className: "flex flex-wrap gap-2", children: config.availableTags.map((tag) => (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)
|
|
17614
17680
|
? "bg-[#C2D1D9] text-white shadow-md shadow-[#C2D1D9]/30"
|
|
17615
17681
|
: "bg-gray-100 text-gray-700 hover:bg-gray-200 hover:shadow-sm"), children: tag }, tag))) })] })), jsxs("div", { className: "flex gap-3 mt-6 pt-6 border-t border-gray-100", children: [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" }), jsx("div", { onClick: (e) => {
|
|
17616
|
-
const isValid = feedbackType === "copy-amendment"
|
|
17682
|
+
const isValid = feedbackType === "copy-amendment"
|
|
17683
|
+
? newCopy.trim()
|
|
17684
|
+
: title.trim();
|
|
17617
17685
|
if (!isValid || isSubmitting)
|
|
17618
17686
|
return;
|
|
17619
17687
|
const form = e.currentTarget.closest("form");
|
|
@@ -17624,12 +17692,18 @@ const FeedbackForm = ({ onSubmit, onCancel, initialData, getSessionEvents, lastC
|
|
|
17624
17692
|
});
|
|
17625
17693
|
form.dispatchEvent(submitEvent);
|
|
17626
17694
|
}
|
|
17627
|
-
}, 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"
|
|
17695
|
+
}, 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"
|
|
17696
|
+
? !newCopy.trim()
|
|
17697
|
+
: !title.trim()) ||
|
|
17698
|
+
isSubmitting) &&
|
|
17628
17699
|
"opacity-50 cursor-not-allowed"), style: {
|
|
17629
|
-
backgroundColor: (feedbackType === "copy-amendment"
|
|
17700
|
+
backgroundColor: (feedbackType === "copy-amendment"
|
|
17701
|
+
? !newCopy.trim()
|
|
17702
|
+
: !title.trim()) || isSubmitting
|
|
17630
17703
|
? "#9ca3af"
|
|
17631
17704
|
: "green-500",
|
|
17632
|
-
}, children: isSubmitting ? (isUploadingVideo ? (jsxs("span", { className: "flex items-center justify-center gap-2", children: [jsx("span", { className: "w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" }), jsx("span", { children: "Uploading video..." })] })) : (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) ||
|
|
17705
|
+
}, children: isSubmitting ? (isUploadingVideo ? (jsxs("span", { className: "flex items-center justify-center gap-2", children: [jsx("span", { className: "w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" }), jsx("span", { children: "Uploading video..." })] })) : (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) ||
|
|
17706
|
+
(initialData ? "Update Feedback" : "Submit Feedback")) })] })] }));
|
|
17633
17707
|
};
|
|
17634
17708
|
|
|
17635
17709
|
const statusOptions$1 = [
|
|
@@ -39762,6 +39836,7 @@ const subscribeFeedback = (projectId, callback) => {
|
|
|
39762
39836
|
componentName: data.componentName,
|
|
39763
39837
|
designMockup: data.designMockup,
|
|
39764
39838
|
screenRecording: data.screenRecording,
|
|
39839
|
+
pinLocation: data.pinLocation,
|
|
39765
39840
|
};
|
|
39766
39841
|
});
|
|
39767
39842
|
callback(feedbackItems);
|
|
@@ -39870,6 +39945,7 @@ const FeedbackDetail = ({ feedback: initialFeedback, onBack, onDelete, onUpdate,
|
|
|
39870
39945
|
const [showFullScreenshot, setShowFullScreenshot] = useState(false);
|
|
39871
39946
|
const [copied, setCopied] = useState(false);
|
|
39872
39947
|
const [isEditing, setIsEditing] = useState(false);
|
|
39948
|
+
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
|
39873
39949
|
// Feature 2 & 5 states
|
|
39874
39950
|
const [showReplay, setShowReplay] = useState(false);
|
|
39875
39951
|
const [showOverlay, setShowOverlay] = useState(false);
|
|
@@ -40023,9 +40099,8 @@ ${feedback.comments && feedback.comments.length > 0
|
|
|
40023
40099
|
setNewComment("");
|
|
40024
40100
|
setIsAddingComment(false);
|
|
40025
40101
|
};
|
|
40026
|
-
const handleDelete =
|
|
40027
|
-
|
|
40028
|
-
return;
|
|
40102
|
+
const handleDelete = () => setShowDeleteConfirm(true);
|
|
40103
|
+
const handleConfirmDelete = async () => {
|
|
40029
40104
|
// Delete from Firebase if initialized
|
|
40030
40105
|
try {
|
|
40031
40106
|
await deleteFeedback(feedback.id);
|
|
@@ -40039,8 +40114,10 @@ ${feedback.comments && feedback.comments.length > 0
|
|
|
40039
40114
|
if (onDelete) {
|
|
40040
40115
|
onDelete(feedback.id);
|
|
40041
40116
|
}
|
|
40117
|
+
setShowDeleteConfirm(false);
|
|
40042
40118
|
};
|
|
40043
|
-
|
|
40119
|
+
const handleCancelDelete = () => setShowDeleteConfirm(false);
|
|
40120
|
+
return (jsxs("div", { className: "flex flex-col h-full px-5 overflow-y-auto", children: [jsxs("div", { className: "flex items-center justify-between pb-3 mb-3 border-b border-gray-200", children: [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: [jsx(ArrowLeftIcon, { className: "w-4 h-4" }), "Back"] }), jsxs("div", { className: "flex items-center gap-2", children: [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 ? (jsxs(Fragment, { children: [jsx(CheckIcon, { className: "w-4 h-4" }), "Copied!"] })) : (jsx(Fragment, { children: jsx(CopyIcon, { className: "w-4 h-4" }) })) }), canEdit && (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: jsx(EditIcon, { className: "w-4 h-4" }) })), canDelete && onDelete && (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: jsx(TrashIcon, { className: "w-4 h-4" }) }))] })] }), showDeleteConfirm && (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: [jsx("div", { className: "flex-1 text-xs font-medium", children: "Delete this feedback?" }), 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" }), 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" })] })), jsxs("div", { className: "flex-1 pr-1", children: [jsxs("div", { className: "mb-4", children: [jsxs("div", { className: "flex items-start gap-2 mb-2", children: [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) && (jsxs("div", { className: "flex-shrink-0 mt-0.5", children: [feedback.pageMetadata.deviceType ===
|
|
40044
40121
|
"desktop" && (jsx(DesktopIcon, { className: "w-5 h-5 text-gray-500", title: "Desktop" })), feedback.pageMetadata.deviceType ===
|
|
40045
40122
|
"tablet" && (jsx(TabletIcon, { className: "w-5 h-5 text-gray-500", title: "Tablet" })), feedback.pageMetadata.deviceType ===
|
|
40046
40123
|
"mobile" && (jsx(MobileIcon, { className: "w-5 h-5 text-gray-500", title: "Mobile" }))] }))] }), jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [feedback.type && (jsx("span", { className: cn("px-2 py-0.5 rounded-full text-[10px] font-medium uppercase", feedback.type === "copy-amendment"
|
|
@@ -40221,6 +40298,89 @@ const ViewportControls = () => {
|
|
|
40221
40298
|
: "hover:bg-gray-100"), children: [jsx(DesktopIcon, { className: cn("w-4 h-4 transition-colors", isActivePreset(preset) ? "text-blue-600" : "text-gray-400 group-hover:text-gray-600") }), jsxs("div", { className: "flex-1", children: [jsx("div", { className: "text-sm font-medium text-gray-700 group-hover:text-gray-900", children: preset.name }), jsxs("div", { className: "text-xs text-gray-500", children: [preset.width, " \u00D7 ", preset.height] })] })] }, preset.name)))] }) }))] }));
|
|
40222
40299
|
};
|
|
40223
40300
|
|
|
40301
|
+
const FeedbackPins = ({ onPinClick }) => {
|
|
40302
|
+
const { feedbackItems, showPins } = useMarkupStore();
|
|
40303
|
+
const [docSize, setDocSize] = useState({ width: 0, height: 0 });
|
|
40304
|
+
useEffect(() => {
|
|
40305
|
+
const measure = () => {
|
|
40306
|
+
const root = document.documentElement;
|
|
40307
|
+
const body = document.body;
|
|
40308
|
+
const width = root.clientWidth || body.clientWidth || window.innerWidth;
|
|
40309
|
+
const height = Math.max(root.scrollHeight, body.scrollHeight, root.clientHeight, body.clientHeight);
|
|
40310
|
+
setDocSize({ width, height });
|
|
40311
|
+
};
|
|
40312
|
+
measure();
|
|
40313
|
+
const resizeObserver = new ResizeObserver(() => measure());
|
|
40314
|
+
resizeObserver.observe(document.documentElement);
|
|
40315
|
+
resizeObserver.observe(document.body);
|
|
40316
|
+
const handleResize = () => measure();
|
|
40317
|
+
const handleScroll = () => measure();
|
|
40318
|
+
window.addEventListener('resize', handleResize);
|
|
40319
|
+
window.addEventListener('scroll', handleScroll, { passive: true });
|
|
40320
|
+
return () => {
|
|
40321
|
+
resizeObserver.disconnect();
|
|
40322
|
+
window.removeEventListener('resize', handleResize);
|
|
40323
|
+
window.removeEventListener('scroll', handleScroll);
|
|
40324
|
+
};
|
|
40325
|
+
}, []);
|
|
40326
|
+
// Filter pins for current page URL
|
|
40327
|
+
const currentPagePins = useMemo(() => {
|
|
40328
|
+
const currentUrl = window.location.href;
|
|
40329
|
+
return feedbackItems.filter(item => {
|
|
40330
|
+
var _a;
|
|
40331
|
+
// Only show pins with pinLocation data
|
|
40332
|
+
if (!item.pinLocation)
|
|
40333
|
+
return false;
|
|
40334
|
+
// Only show pins from the same page
|
|
40335
|
+
if (((_a = item.pageMetadata) === null || _a === void 0 ? void 0 : _a.url) !== currentUrl)
|
|
40336
|
+
return false;
|
|
40337
|
+
return true;
|
|
40338
|
+
});
|
|
40339
|
+
}, [feedbackItems]);
|
|
40340
|
+
// Don't show pins if toggle is off
|
|
40341
|
+
if (!showPins)
|
|
40342
|
+
return null;
|
|
40343
|
+
if (currentPagePins.length === 0)
|
|
40344
|
+
return null;
|
|
40345
|
+
const containerStyle = {
|
|
40346
|
+
position: 'absolute',
|
|
40347
|
+
top: 0,
|
|
40348
|
+
left: 0,
|
|
40349
|
+
width: docSize.width || '100%',
|
|
40350
|
+
height: docSize.height || '100%',
|
|
40351
|
+
pointerEvents: 'none',
|
|
40352
|
+
zIndex: 999998,
|
|
40353
|
+
};
|
|
40354
|
+
return (jsx("div", { className: "feedback-pins-container", "data-markup-pins": true, style: containerStyle, children: currentPagePins.map((feedback) => {
|
|
40355
|
+
const { pinLocation } = feedback;
|
|
40356
|
+
if (!pinLocation)
|
|
40357
|
+
return null;
|
|
40358
|
+
const pageWidth = docSize.width || pinLocation.pageWidth || pinLocation.viewportWidth || window.innerWidth;
|
|
40359
|
+
const pageHeight = docSize.height || pinLocation.pageHeight || pinLocation.viewportHeight || window.innerHeight;
|
|
40360
|
+
// Prefer stored percentages; fall back to computing from stored absolute coords
|
|
40361
|
+
const leftPercent = typeof pinLocation.percentX === 'number'
|
|
40362
|
+
? pinLocation.percentX
|
|
40363
|
+
: pageWidth ? (pinLocation.pageX / pageWidth) * 100 : 0;
|
|
40364
|
+
const topPercent = typeof pinLocation.percentY === 'number'
|
|
40365
|
+
? pinLocation.percentY
|
|
40366
|
+
: pageHeight ? (pinLocation.pageY / pageHeight) * 100 : 0;
|
|
40367
|
+
const style = {
|
|
40368
|
+
position: 'absolute',
|
|
40369
|
+
left: `${leftPercent}%`,
|
|
40370
|
+
top: `${topPercent}%`,
|
|
40371
|
+
transform: 'translate(-50%, -50%)',
|
|
40372
|
+
pointerEvents: 'auto',
|
|
40373
|
+
};
|
|
40374
|
+
return (jsx("div", { className: "feedback-pin-wrapper", style: style, onClick: () => onPinClick(feedback), children: jsxs("div", { className: "feedback-pin group relative cursor-pointer", children: [jsx("div", { className: `
|
|
40375
|
+
w-8 h-8 rounded-full flex items-center justify-center
|
|
40376
|
+
shadow-lg hover:shadow-xl transition-all
|
|
40377
|
+
${feedback.status === 'open' ? 'bg-[#E6B6CF] hover:bg-[#d9a3c0]' : 'bg-green-500 hover:bg-green-600'}
|
|
40378
|
+
border-2 border-white
|
|
40379
|
+
hover:scale-110
|
|
40380
|
+
`, children: jsx(MessageIcon, { className: "w-4 h-4 text-white" }) }), 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: [jsx("div", { className: "font-semibold truncate", children: feedback.title || 'Feedback' }), jsx("div", { className: "text-gray-300 text-[10px] mt-0.5", children: "Click to view" }), 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));
|
|
40381
|
+
}) }));
|
|
40382
|
+
};
|
|
40383
|
+
|
|
40224
40384
|
const useFirebaseSync = () => {
|
|
40225
40385
|
const config = useMarkupStore((state) => state.config);
|
|
40226
40386
|
const setCurrentUser = useMarkupStore((state) => state.setCurrentUser);
|
|
@@ -40432,7 +40592,7 @@ const MarkupWidget = ({ config: userConfig, }) => {
|
|
|
40432
40592
|
console.error('Failed to initialize Firebase:', error);
|
|
40433
40593
|
}
|
|
40434
40594
|
}
|
|
40435
|
-
const { isOpen, setIsOpen, activeTab, setActiveTab, selectedFeedback, setSelectedFeedback, feedbackItems, addFeedbackItem, removeFeedbackItem, updateFeedback, config, setConfig, reset, isAuthenticated, currentUser, viewportMode, setViewportMode, } = useMarkupStore();
|
|
40595
|
+
const { isOpen, setIsOpen, activeTab, setActiveTab, selectedFeedback, setSelectedFeedback, feedbackItems, addFeedbackItem, removeFeedbackItem, updateFeedback, config, setConfig, reset, isAuthenticated, currentUser, viewportMode, setViewportMode, showPins, setShowPins, } = useMarkupStore();
|
|
40436
40596
|
// Initialize Firebase sync
|
|
40437
40597
|
useFirebaseSync();
|
|
40438
40598
|
// Initialize Session Recording (Feature 2)
|
|
@@ -40571,6 +40731,11 @@ const MarkupWidget = ({ config: userConfig, }) => {
|
|
|
40571
40731
|
const handleSelectFeedback = useCallback((feedback) => {
|
|
40572
40732
|
setSelectedFeedback(feedback);
|
|
40573
40733
|
}, [setSelectedFeedback]);
|
|
40734
|
+
const handlePinClick = useCallback((feedback) => {
|
|
40735
|
+
setIsOpen(true);
|
|
40736
|
+
setActiveTab("list");
|
|
40737
|
+
setSelectedFeedback(feedback);
|
|
40738
|
+
}, [setIsOpen, setActiveTab, setSelectedFeedback]);
|
|
40574
40739
|
const handleBackFromDetail = useCallback(() => {
|
|
40575
40740
|
setSelectedFeedback(null);
|
|
40576
40741
|
setViewportMode(null);
|
|
@@ -40614,11 +40779,14 @@ const MarkupWidget = ({ config: userConfig, }) => {
|
|
|
40614
40779
|
? adjustColor(config.primaryColor, -20)
|
|
40615
40780
|
: "#4f46e5",
|
|
40616
40781
|
});
|
|
40617
|
-
return (jsxs(Fragment, { children: [jsx(ViewportControls, {}), 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: {
|
|
40782
|
+
return (jsxs(Fragment, { children: [jsx(FeedbackPins, { onPinClick: handlePinClick }), jsx(ViewportControls, {}), 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: {
|
|
40618
40783
|
transform: isOpen
|
|
40619
40784
|
? "translateY(-50%) translateX(0)"
|
|
40620
40785
|
: "translateY(-50%) translateX(calc(100% - 40px))",
|
|
40621
|
-
}, "data-markup-widget": true, children: [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 && jsx(CloseIcon, { className: "w-5 h-5" }), jsx("span", { className: "text-black", style: { writingMode: "sideways-lr" }, children: "Siren" }), openFeedbackCount > 0 && (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 }))] }), jsxs("div", { className: "bg-white w-[500px] h-[750px] rounded-l-xl shadow-2xl overflow-hidden flex flex-col text-black", children: [jsxs("div", { className: "flex items-center justify-between px-5 py-4 text-black", style: { backgroundColor: "var(--markup-primary)" }, children: [jsx("h2", { className: "text-base font-semibold m-0", children: ((_a = config.labels) === null || _a === void 0 ? void 0 : _a.feedbackTitle) || "Feedback" }),
|
|
40786
|
+
}, "data-markup-widget": true, children: [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 && jsx(CloseIcon, { className: "w-5 h-5" }), jsx("span", { className: "text-black", style: { writingMode: "sideways-lr" }, children: "Siren" }), openFeedbackCount > 0 && (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 }))] }), jsxs("div", { className: "bg-white w-[500px] h-[750px] rounded-l-xl shadow-2xl overflow-hidden flex flex-col text-black", children: [jsxs("div", { className: "flex items-center justify-between px-5 py-4 text-black", style: { backgroundColor: "var(--markup-primary)" }, children: [jsx("h2", { className: "text-base font-semibold m-0", children: ((_a = config.labels) === null || _a === void 0 ? void 0 : _a.feedbackTitle) || "Feedback" }), jsxs("div", { className: "flex items-center gap-2", children: [jsxs("div", { onClick: (e) => {
|
|
40787
|
+
e.stopPropagation();
|
|
40788
|
+
setShowPins(!showPins);
|
|
40789
|
+
}, 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: [jsxs("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [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" }), 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 && (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: jsx("h2", { children: "Sign Out" }) }))] })] }), !isAuthenticated ? (jsx("div", { className: "flex-1 overflow-y-auto", children: jsx(AuthForm, { onSuccess: handleAuthSuccess }) })) : selectedFeedback ? (jsx(FeedbackDetail, { feedback: selectedFeedback, onBack: handleBackFromDetail, onDelete: handleDeleteFeedback, onUpdate: handleUpdateFeedback })) : (jsxs(Fragment, { children: [jsxs("div", { className: "flex border-b border-gray-200 px-5", children: [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"
|
|
40622
40790
|
? "bg-gray-200"
|
|
40623
40791
|
: "border-white"), onClick: () => setActiveTab("create"), children: [jsx(PlusIcon, { className: "w-3.5 h-3.5" }), "New"] }), 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"
|
|
40624
40792
|
? "bg-gray-200"
|