galadrim-feedback 0.0.1
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/README.md +0 -0
- package/dist/index.cjs.js +56877 -0
- package/dist/index.es.js +56875 -0
- package/dist/src/index.css +7 -0
- package/dist/types/App.d.ts +3 -0
- package/dist/types/components/buttons/IconButton.d.ts +6 -0
- package/dist/types/components/buttons/PlusButton.d.ts +5 -0
- package/dist/types/components/feedback/FeedbackItem.d.ts +4 -0
- package/dist/types/components/misc/Avatar.d.ts +3 -0
- package/dist/types/components/misc/Modal.d.ts +6 -0
- package/dist/types/pages/FeedbacksCanvas.d.ts +7 -0
- package/dist/types/pages/Overlay.d.ts +9 -0
- package/dist/types/pages/Root.d.ts +2 -0
- package/dist/types/services/api.d.ts +2 -0
- package/dist/types/services/feedback.d.ts +9 -0
- package/eslint.config.js +28 -0
- package/package.json +47 -0
- package/rollup.config.js +39 -0
- package/src/App.tsx +16 -0
- package/src/components/buttons/IconButton.tsx +27 -0
- package/src/components/buttons/PlusButton.tsx +20 -0
- package/src/components/feedback/FeedbackItem.tsx +89 -0
- package/src/components/misc/Avatar.tsx +3 -0
- package/src/components/misc/Modal.tsx +43 -0
- package/src/index.css +7 -0
- package/src/pages/FeedbacksCanvas.tsx +38 -0
- package/src/pages/Overlay.tsx +26 -0
- package/src/pages/Root.tsx +128 -0
- package/src/services/api.ts +22 -0
- package/src/services/feedback.ts +62 -0
- package/src/types/types.d.ts +16 -0
- package/tailwind.config.js +46 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Dispatch } from "react";
|
|
2
|
+
import { Feedback } from "../types/types";
|
|
3
|
+
export declare const FeedbacksCanvas: ({ isOpen, setIsOpen, feedbacks, }: {
|
|
4
|
+
isOpen: boolean;
|
|
5
|
+
setIsOpen: Dispatch<React.SetStateAction<boolean>>;
|
|
6
|
+
feedbacks: Feedback[];
|
|
7
|
+
}) => import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
2
|
+
import { Dispatch } from "react";
|
|
3
|
+
import { Feedback } from "../types/types";
|
|
4
|
+
export declare const Overlay: ({ isOpen, setIsOpen, queryClient, feedbacks, }: {
|
|
5
|
+
isOpen: boolean;
|
|
6
|
+
setIsOpen: Dispatch<React.SetStateAction<boolean>>;
|
|
7
|
+
queryClient: QueryClient;
|
|
8
|
+
feedbacks: Feedback[];
|
|
9
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Feedback, FeedbackPayload } from "../types/types";
|
|
2
|
+
export declare const fetchFeedbacks: () => Promise<Feedback[]>;
|
|
3
|
+
export declare const createFeedback: (feedback: FeedbackPayload) => Promise<any>;
|
|
4
|
+
export declare const updateFeedback: (feedback: FeedbackPayload) => Promise<any>;
|
|
5
|
+
export declare const deleteFeedback: (feedback: Feedback) => Promise<any>;
|
|
6
|
+
export declare const useFeedbacks: () => import("@tanstack/react-query").UseQueryResult<Feedback[], Error>;
|
|
7
|
+
export declare const useCreateFeedback: () => import("@tanstack/react-query").UseMutationResult<any, Error, FeedbackPayload, unknown>;
|
|
8
|
+
export declare const useUpdateFeedback: () => import("@tanstack/react-query").UseMutationResult<any, Error, FeedbackPayload, unknown>;
|
|
9
|
+
export declare const useDeleteFeedback: () => import("@tanstack/react-query").UseMutationResult<any, Error, Feedback, unknown>;
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import js from '@eslint/js'
|
|
2
|
+
import globals from 'globals'
|
|
3
|
+
import reactHooks from 'eslint-plugin-react-hooks'
|
|
4
|
+
import reactRefresh from 'eslint-plugin-react-refresh'
|
|
5
|
+
import tseslint from 'typescript-eslint'
|
|
6
|
+
|
|
7
|
+
export default tseslint.config(
|
|
8
|
+
{ ignores: ['dist'] },
|
|
9
|
+
{
|
|
10
|
+
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
|
11
|
+
files: ['**/*.{ts,tsx}'],
|
|
12
|
+
languageOptions: {
|
|
13
|
+
ecmaVersion: 2020,
|
|
14
|
+
globals: globals.browser,
|
|
15
|
+
},
|
|
16
|
+
plugins: {
|
|
17
|
+
'react-hooks': reactHooks,
|
|
18
|
+
'react-refresh': reactRefresh,
|
|
19
|
+
},
|
|
20
|
+
rules: {
|
|
21
|
+
...reactHooks.configs.recommended.rules,
|
|
22
|
+
'react-refresh/only-export-components': [
|
|
23
|
+
'warn',
|
|
24
|
+
{ allowConstantExport: true },
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
)
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "galadrim-feedback",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.cjs.js",
|
|
7
|
+
"module": "dist/index.es.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "rollup -c",
|
|
11
|
+
"prepublishOnly": "npm run build"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@tanstack/react-query": "^5.59.19",
|
|
15
|
+
"axios": "^1.7.7",
|
|
16
|
+
"classnames": "^2.5.1",
|
|
17
|
+
"moment": "^2.30.1",
|
|
18
|
+
"react": "^18.3.1",
|
|
19
|
+
"react-dom": "^18.3.1",
|
|
20
|
+
"react-router-dom": "^6.26.2"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@eslint/js": "^9.11.1",
|
|
24
|
+
"@rollup/plugin-commonjs": "^28.0.2",
|
|
25
|
+
"@rollup/plugin-json": "^6.1.0",
|
|
26
|
+
"@rollup/plugin-node-resolve": "^16.0.0",
|
|
27
|
+
"@rollup/plugin-typescript": "^12.1.2",
|
|
28
|
+
"@types/react": "^18.3.10",
|
|
29
|
+
"@types/react-dom": "^18.3.0",
|
|
30
|
+
"@vitejs/plugin-react": "^4.3.2",
|
|
31
|
+
"autoprefixer": "^10.4.20",
|
|
32
|
+
"babel-plugin-react-generate-property": "^1.1.2",
|
|
33
|
+
"eslint": "^9.11.1",
|
|
34
|
+
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
|
|
35
|
+
"eslint-plugin-react-refresh": "^0.4.12",
|
|
36
|
+
"globals": "^15.9.0",
|
|
37
|
+
"postcss": "^8.4.49",
|
|
38
|
+
"rollup": "^4.30.1",
|
|
39
|
+
"rollup-plugin-postcss": "^4.0.2",
|
|
40
|
+
"sass": "^1.83.1",
|
|
41
|
+
"tailwindcss": "^3.4.13",
|
|
42
|
+
"tslib": "^2.8.1",
|
|
43
|
+
"typescript": "^5.5.3",
|
|
44
|
+
"typescript-eslint": "^8.7.0",
|
|
45
|
+
"vite": "^5.4.8"
|
|
46
|
+
}
|
|
47
|
+
}
|
package/rollup.config.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// rollup.config.js
|
|
2
|
+
import commonjs from "@rollup/plugin-commonjs";
|
|
3
|
+
import resolve from "@rollup/plugin-node-resolve";
|
|
4
|
+
import typescript from "@rollup/plugin-typescript";
|
|
5
|
+
import postcss from "rollup-plugin-postcss";
|
|
6
|
+
import json from "@rollup/plugin-json";
|
|
7
|
+
|
|
8
|
+
import pkg from "./package.json" assert { type: "json" };
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
input: "src/App.tsx", // Your entry point
|
|
12
|
+
output: [
|
|
13
|
+
{
|
|
14
|
+
file: pkg.main, // dist/index.cjs.js
|
|
15
|
+
format: "cjs",
|
|
16
|
+
sourcemap: "inline",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
file: pkg.module, // dist/index.es.js
|
|
20
|
+
format: "es",
|
|
21
|
+
sourcemap: "inline",
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
external: [
|
|
25
|
+
// Peer dependencies (don’t bundle React, ReactDOM, etc.)
|
|
26
|
+
...Object.keys(pkg.peerDependencies || {}),
|
|
27
|
+
],
|
|
28
|
+
plugins: [
|
|
29
|
+
resolve(),
|
|
30
|
+
commonjs(),
|
|
31
|
+
postcss({
|
|
32
|
+
extract: "src/index.css",
|
|
33
|
+
}),
|
|
34
|
+
typescript({
|
|
35
|
+
tsconfig: "./tsconfig.json",
|
|
36
|
+
}),
|
|
37
|
+
json(),
|
|
38
|
+
],
|
|
39
|
+
};
|
package/src/App.tsx
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
2
|
+
import Root from "./pages/Root";
|
|
3
|
+
import "./index.css";
|
|
4
|
+
import React from "react";
|
|
5
|
+
|
|
6
|
+
const queryClient = new QueryClient();
|
|
7
|
+
|
|
8
|
+
function App() {
|
|
9
|
+
return (
|
|
10
|
+
<QueryClientProvider client={queryClient}>
|
|
11
|
+
<Root />
|
|
12
|
+
</QueryClientProvider>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default App;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import classNames from "classnames";
|
|
2
|
+
|
|
3
|
+
export const IconButton = ({
|
|
4
|
+
text,
|
|
5
|
+
iconPath,
|
|
6
|
+
twButtonBackgroundColor,
|
|
7
|
+
onClick,
|
|
8
|
+
}: {
|
|
9
|
+
text: string;
|
|
10
|
+
iconPath: string;
|
|
11
|
+
twButtonBackgroundColor: string;
|
|
12
|
+
onClick: () => void;
|
|
13
|
+
}) => {
|
|
14
|
+
return (
|
|
15
|
+
<button
|
|
16
|
+
onClick={onClick}
|
|
17
|
+
className={classNames(
|
|
18
|
+
"flex-shrink-0 flex flex-row items-center px-4 py-2 gap-2 rounded-[8px] text-white font-[500] ",
|
|
19
|
+
twButtonBackgroundColor,
|
|
20
|
+
"hover:bg-opacity-80"
|
|
21
|
+
)}
|
|
22
|
+
>
|
|
23
|
+
<img src={iconPath} className="w-6 h-6" />
|
|
24
|
+
{text}
|
|
25
|
+
</button>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { IconButton } from "./IconButton";
|
|
2
|
+
|
|
3
|
+
export const PlusButton = ({
|
|
4
|
+
text,
|
|
5
|
+
twButtonBackgroundColor,
|
|
6
|
+
onClick,
|
|
7
|
+
}: {
|
|
8
|
+
text: string;
|
|
9
|
+
twButtonBackgroundColor: string;
|
|
10
|
+
onClick: () => void;
|
|
11
|
+
}) => {
|
|
12
|
+
return (
|
|
13
|
+
<IconButton
|
|
14
|
+
text={text}
|
|
15
|
+
iconPath="/plus.svg"
|
|
16
|
+
twButtonBackgroundColor={twButtonBackgroundColor}
|
|
17
|
+
onClick={onClick}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { createPortal } from "react-dom";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { Feedback } from "../../types/types";
|
|
4
|
+
import { useDeleteFeedback } from "../../services/feedback";
|
|
5
|
+
|
|
6
|
+
export const FeedbackItem = ({ feedback }: { feedback: Feedback }) => {
|
|
7
|
+
const targetElement = useRef<HTMLElement | null>(null);
|
|
8
|
+
const { mutate: deleteFeedback } = useDeleteFeedback();
|
|
9
|
+
const [position, setPosition] = useState({ top: 0, left: 0 });
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const handleResize = () => {
|
|
13
|
+
if (!targetElement || !targetElement.current) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const dimensions = targetElement.current.getBoundingClientRect();
|
|
17
|
+
setPosition({
|
|
18
|
+
top: feedback.y + dimensions.top + window.scrollY,
|
|
19
|
+
left: feedback.x + dimensions.left + window.scrollX,
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const elements = document.querySelectorAll<HTMLElement>(
|
|
24
|
+
`[data-id="${feedback.elementId}"]`
|
|
25
|
+
);
|
|
26
|
+
const element = elements[feedback.elementIndex];
|
|
27
|
+
|
|
28
|
+
if (element) {
|
|
29
|
+
console.log(element);
|
|
30
|
+
|
|
31
|
+
targetElement.current = element;
|
|
32
|
+
handleResize();
|
|
33
|
+
window.addEventListener("resize", handleResize);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return () => {
|
|
37
|
+
if (element) {
|
|
38
|
+
window.removeEventListener("resize", handleResize);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}, [feedback]);
|
|
42
|
+
|
|
43
|
+
const handleDeleteFeedback = () => {
|
|
44
|
+
deleteFeedback(feedback);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// If we haven't found a matching DOM node, don't render anything
|
|
48
|
+
if (!targetElement || !targetElement.current) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
console.log("rendrr", position);
|
|
53
|
+
|
|
54
|
+
// Render this component inside `targetElement` using a Portal
|
|
55
|
+
return createPortal(
|
|
56
|
+
<div
|
|
57
|
+
style={{
|
|
58
|
+
position: "absolute",
|
|
59
|
+
top: position.top,
|
|
60
|
+
left: position.left,
|
|
61
|
+
width: "200px",
|
|
62
|
+
height: "100px",
|
|
63
|
+
border: "1px solid black",
|
|
64
|
+
backgroundColor: "white",
|
|
65
|
+
}}
|
|
66
|
+
>
|
|
67
|
+
<div
|
|
68
|
+
style={{
|
|
69
|
+
display: "flex",
|
|
70
|
+
justifyContent: "space-between",
|
|
71
|
+
alignItems: "center",
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
<div>
|
|
75
|
+
<p style={{ fontSize: "16px", fontWeight: "bold" }}>Feedback</p>
|
|
76
|
+
<p style={{ fontSize: "14px" }}>
|
|
77
|
+
Created at: {feedback.createdAt.toLocaleString()}
|
|
78
|
+
</p>
|
|
79
|
+
<a href={feedback.elementId} target="_blank">
|
|
80
|
+
Go to component
|
|
81
|
+
</a>
|
|
82
|
+
</div>
|
|
83
|
+
<button onClick={handleDeleteFeedback}>Delete</button>
|
|
84
|
+
</div>
|
|
85
|
+
<p style={{ fontSize: "14px" }}>{feedback.comment}</p>
|
|
86
|
+
</div>,
|
|
87
|
+
document.body
|
|
88
|
+
);
|
|
89
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useRef } from "react";
|
|
2
|
+
|
|
3
|
+
export const Modal = ({
|
|
4
|
+
isVisible,
|
|
5
|
+
closeModal,
|
|
6
|
+
limitWidth = true,
|
|
7
|
+
children,
|
|
8
|
+
}: {
|
|
9
|
+
isVisible: boolean;
|
|
10
|
+
closeModal?: () => void;
|
|
11
|
+
limitWidth?: boolean;
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
}) => {
|
|
14
|
+
const resizing = useRef(false);
|
|
15
|
+
|
|
16
|
+
if (!isVisible) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const width = limitWidth ? "w-[400px]" : "";
|
|
20
|
+
return (
|
|
21
|
+
<div
|
|
22
|
+
className="modal"
|
|
23
|
+
onMouseDown={(e) => {
|
|
24
|
+
const target = e.target as HTMLElement;
|
|
25
|
+
if (target.tagName === "TEXTAREA") {
|
|
26
|
+
resizing.current = true;
|
|
27
|
+
}
|
|
28
|
+
}}
|
|
29
|
+
onMouseUp={() => {
|
|
30
|
+
setTimeout(() => {
|
|
31
|
+
resizing.current = false;
|
|
32
|
+
}, 100);
|
|
33
|
+
}}
|
|
34
|
+
onClick={(e) => {
|
|
35
|
+
if (closeModal && e.target === e.currentTarget && !resizing.current) {
|
|
36
|
+
closeModal();
|
|
37
|
+
}
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
<div className={`${width} bg-white rounded-lg`}>{children}</div>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
};
|
package/src/index.css
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Dispatch, useEffect } from "react";
|
|
2
|
+
import { FeedbackItem } from "../components/feedback/FeedbackItem";
|
|
3
|
+
import { useFeedbacks } from "../services/feedback";
|
|
4
|
+
import { Feedback } from "../types/types";
|
|
5
|
+
|
|
6
|
+
export const FeedbacksCanvas = ({
|
|
7
|
+
isOpen,
|
|
8
|
+
setIsOpen,
|
|
9
|
+
feedbacks,
|
|
10
|
+
}: {
|
|
11
|
+
isOpen: boolean;
|
|
12
|
+
setIsOpen: Dispatch<React.SetStateAction<boolean>>;
|
|
13
|
+
feedbacks: Feedback[];
|
|
14
|
+
}) => {
|
|
15
|
+
if (!isOpen) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
style={{
|
|
22
|
+
zIndex: 2147483647, // Maximum zIndex
|
|
23
|
+
position: "fixed",
|
|
24
|
+
top: 0,
|
|
25
|
+
left: 0,
|
|
26
|
+
width: "100vw",
|
|
27
|
+
height: "100vh",
|
|
28
|
+
}}
|
|
29
|
+
>
|
|
30
|
+
{feedbacks &&
|
|
31
|
+
feedbacks
|
|
32
|
+
.filter((feedback) => feedback.path === window.location.pathname)
|
|
33
|
+
.map((feedback) => (
|
|
34
|
+
<FeedbackItem key={feedback.id} feedback={feedback} />
|
|
35
|
+
))}
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
2
|
+
import { Dispatch } from "react";
|
|
3
|
+
import { FeedbacksCanvas } from "./FeedbacksCanvas";
|
|
4
|
+
import { Feedback } from "../types/types";
|
|
5
|
+
|
|
6
|
+
export const Overlay = ({
|
|
7
|
+
isOpen,
|
|
8
|
+
setIsOpen,
|
|
9
|
+
queryClient,
|
|
10
|
+
feedbacks,
|
|
11
|
+
}: {
|
|
12
|
+
isOpen: boolean;
|
|
13
|
+
setIsOpen: Dispatch<React.SetStateAction<boolean>>;
|
|
14
|
+
queryClient: QueryClient;
|
|
15
|
+
feedbacks: Feedback[];
|
|
16
|
+
}) => {
|
|
17
|
+
return (
|
|
18
|
+
<QueryClientProvider client={queryClient}>
|
|
19
|
+
<FeedbacksCanvas
|
|
20
|
+
isOpen={isOpen}
|
|
21
|
+
setIsOpen={setIsOpen}
|
|
22
|
+
feedbacks={feedbacks}
|
|
23
|
+
/>
|
|
24
|
+
</QueryClientProvider>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
2
|
+
import React, { useEffect } from "react";
|
|
3
|
+
import ReactDOM from "react-dom";
|
|
4
|
+
import { Overlay } from "./Overlay";
|
|
5
|
+
import { useCreateFeedback, useFeedbacks } from "../services/feedback";
|
|
6
|
+
import { FeedbackPayload } from "../types/types";
|
|
7
|
+
import { FeedbackItem } from "../components/feedback/FeedbackItem";
|
|
8
|
+
|
|
9
|
+
const queryClient = new QueryClient();
|
|
10
|
+
|
|
11
|
+
function Root() {
|
|
12
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
13
|
+
const { data: feedbacks } = useFeedbacks();
|
|
14
|
+
|
|
15
|
+
console.log("feedbacks 1 : ", feedbacks);
|
|
16
|
+
|
|
17
|
+
const [creatingFeedback, setCreatingFeedback] = React.useState(false);
|
|
18
|
+
|
|
19
|
+
const { mutate: createFeedback } = useCreateFeedback();
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (isOpen) {
|
|
23
|
+
console.log("Opening feedbacks canvas");
|
|
24
|
+
|
|
25
|
+
window.addEventListener("keydown", handleKeyPress);
|
|
26
|
+
return () => {
|
|
27
|
+
window.removeEventListener("keypress", handleKeyPress);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}, [isOpen]);
|
|
31
|
+
|
|
32
|
+
const handleKeyPress = (event: KeyboardEvent) => {
|
|
33
|
+
console.log(event.key);
|
|
34
|
+
|
|
35
|
+
if (event.key === "Escape") {
|
|
36
|
+
setIsOpen(false);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const handleClick = (e: any) => {
|
|
41
|
+
console.log("click");
|
|
42
|
+
|
|
43
|
+
e.stopPropagation();
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
const node = e.target as HTMLElement | null;
|
|
46
|
+
const elementId = node?.dataset.id || "";
|
|
47
|
+
const allElements = document.querySelectorAll<HTMLElement>(
|
|
48
|
+
`[data-id="${elementId}"]`
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const arrAllElements = Array.from(allElements);
|
|
52
|
+
|
|
53
|
+
const elementIndex = arrAllElements.indexOf(node as HTMLElement);
|
|
54
|
+
|
|
55
|
+
const x = e.offsetX;
|
|
56
|
+
const y = e.offsetY;
|
|
57
|
+
console.log(e);
|
|
58
|
+
const newFeedback: FeedbackPayload = {
|
|
59
|
+
x,
|
|
60
|
+
y,
|
|
61
|
+
screenWidth: window.innerWidth,
|
|
62
|
+
screenHeight: window.innerHeight,
|
|
63
|
+
comment: "test",
|
|
64
|
+
path: window.location.pathname,
|
|
65
|
+
elementId,
|
|
66
|
+
elementIndex,
|
|
67
|
+
};
|
|
68
|
+
console.log(newFeedback);
|
|
69
|
+
createFeedback(newFeedback);
|
|
70
|
+
document.body.classList.remove("comment-cursor");
|
|
71
|
+
document.removeEventListener("click", handleClick, true);
|
|
72
|
+
setCreatingFeedback(false);
|
|
73
|
+
setIsOpen(true);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const handleCreateFeedback = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
77
|
+
e.stopPropagation();
|
|
78
|
+
setCreatingFeedback(true);
|
|
79
|
+
document.body.classList.add("comment-cursor");
|
|
80
|
+
document.addEventListener("click", handleClick, true);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<>
|
|
85
|
+
<div
|
|
86
|
+
style={{
|
|
87
|
+
position: "fixed",
|
|
88
|
+
top: "calc(100vh - 62px)",
|
|
89
|
+
left: "calc(100vw - 124px)",
|
|
90
|
+
width: "100px",
|
|
91
|
+
height: "50px",
|
|
92
|
+
zIndex: 1000000,
|
|
93
|
+
backgroundColor: "#0085FF",
|
|
94
|
+
color: "white",
|
|
95
|
+
borderRadius: 4,
|
|
96
|
+
}}
|
|
97
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
98
|
+
>
|
|
99
|
+
Show Feedback
|
|
100
|
+
</div>
|
|
101
|
+
<button
|
|
102
|
+
style={{
|
|
103
|
+
position: "fixed",
|
|
104
|
+
top: "calc(100vh - 122px)",
|
|
105
|
+
left: "calc(100vw - 124px)",
|
|
106
|
+
width: "100px",
|
|
107
|
+
height: "50px",
|
|
108
|
+
zIndex: 1000000,
|
|
109
|
+
backgroundColor: "#0085FF",
|
|
110
|
+
color: "white",
|
|
111
|
+
borderRadius: 4,
|
|
112
|
+
}}
|
|
113
|
+
onClick={handleCreateFeedback}
|
|
114
|
+
>
|
|
115
|
+
New Feedback
|
|
116
|
+
</button>
|
|
117
|
+
{isOpen &&
|
|
118
|
+
feedbacks &&
|
|
119
|
+
feedbacks
|
|
120
|
+
.filter((feedback) => feedback.path === window.location.pathname)
|
|
121
|
+
.map((feedback) => (
|
|
122
|
+
<FeedbackItem key={feedback.id} feedback={feedback} />
|
|
123
|
+
))}
|
|
124
|
+
</>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export default Root;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
|
|
3
|
+
const api = axios.create({
|
|
4
|
+
baseURL: "http://localhost:3000",
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
api.interceptors.request.use(
|
|
8
|
+
async (config) => {
|
|
9
|
+
// const token = localStorage.getItem("dashboardGaladrimAuthToken");
|
|
10
|
+
// if (token && config.headers) {
|
|
11
|
+
// config.headers["x-dashboard-token"] = token;
|
|
12
|
+
// } else {
|
|
13
|
+
// delete api.defaults.headers.common.Authorization;
|
|
14
|
+
// }
|
|
15
|
+
return config;
|
|
16
|
+
},
|
|
17
|
+
(error) => {
|
|
18
|
+
return Promise.reject(error);
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export default api;
|