mirror of
https://github.com/AykutSarac/jsoncrack.com.git
synced 2025-02-04 01:32:54 +08:00
feat: add ga events
This commit is contained in:
parent
e4d9fff148
commit
4a5dcfb704
@ -15,6 +15,7 @@ import { BiSolidDockLeft } from "react-icons/bi";
|
|||||||
import { MdOutlineCheckCircleOutline } from "react-icons/md";
|
import { MdOutlineCheckCircleOutline } from "react-icons/md";
|
||||||
import { TbTransform } from "react-icons/tb";
|
import { TbTransform } from "react-icons/tb";
|
||||||
import { VscError, VscFeedback, VscSourceControl, VscSync, VscSyncIgnored } from "react-icons/vsc";
|
import { VscError, VscFeedback, VscSourceControl, VscSync, VscSyncIgnored } from "react-icons/vsc";
|
||||||
|
import { gaEvent } from "src/lib/utils/gaEvent";
|
||||||
import { documentSvc } from "src/services/document.service";
|
import { documentSvc } from "src/services/document.service";
|
||||||
import useConfig from "src/store/useConfig";
|
import useConfig from "src/store/useConfig";
|
||||||
import useFile from "src/store/useFile";
|
import useFile from "src/store/useFile";
|
||||||
@ -106,7 +107,10 @@ export const BottomBar = () => {
|
|||||||
const [isPrivate, setIsPrivate] = React.useState(false);
|
const [isPrivate, setIsPrivate] = React.useState(false);
|
||||||
const [isUpdating, setIsUpdating] = React.useState(false);
|
const [isUpdating, setIsUpdating] = React.useState(false);
|
||||||
|
|
||||||
const toggleEditor = () => toggleFullscreen(!fullscreen);
|
const toggleEditor = () => {
|
||||||
|
toggleFullscreen(!fullscreen);
|
||||||
|
gaEvent("Bottom Bar", "toggle fullscreen");
|
||||||
|
};
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setIsPrivate(data?.private ?? true);
|
setIsPrivate(data?.private ?? true);
|
||||||
@ -239,12 +243,22 @@ export const BottomBar = () => {
|
|||||||
Share
|
Share
|
||||||
</StyledBottomBarItem>
|
</StyledBottomBarItem>
|
||||||
{liveTransformEnabled ? (
|
{liveTransformEnabled ? (
|
||||||
<StyledBottomBarItem onClick={() => toggleLiveTransform(false)}>
|
<StyledBottomBarItem
|
||||||
|
onClick={() => {
|
||||||
|
toggleLiveTransform(false);
|
||||||
|
gaEvent("Bottom Bar", "toggle live transform", "manual");
|
||||||
|
}}
|
||||||
|
>
|
||||||
<VscSync />
|
<VscSync />
|
||||||
<Text fz="xs">Live Transform</Text>
|
<Text fz="xs">Live Transform</Text>
|
||||||
</StyledBottomBarItem>
|
</StyledBottomBarItem>
|
||||||
) : (
|
) : (
|
||||||
<StyledBottomBarItem onClick={() => toggleLiveTransform(true)}>
|
<StyledBottomBarItem
|
||||||
|
onClick={() => {
|
||||||
|
toggleLiveTransform(true);
|
||||||
|
gaEvent("Bottom Bar", "toggle live transform", "live");
|
||||||
|
}}
|
||||||
|
>
|
||||||
<VscSyncIgnored />
|
<VscSyncIgnored />
|
||||||
<Text fz="xs">Manual Transform</Text>
|
<Text fz="xs">Manual Transform</Text>
|
||||||
</StyledBottomBarItem>
|
</StyledBottomBarItem>
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
Badge,
|
Badge,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { IoRocketSharp } from "react-icons/io5";
|
import { IoRocketSharp } from "react-icons/io5";
|
||||||
|
import { gaEvent } from "src/lib/utils/gaEvent";
|
||||||
import useModal from "src/store/useModal";
|
import useModal from "src/store/useModal";
|
||||||
import useUser from "src/store/useUser";
|
import useUser from "src/store/useUser";
|
||||||
|
|
||||||
@ -62,7 +63,10 @@ export const AccountModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
|||||||
variant="gradient"
|
variant="gradient"
|
||||||
gradient={{ from: "teal", to: "lime", deg: 105 }}
|
gradient={{ from: "teal", to: "lime", deg: 105 }}
|
||||||
leftSection={<IoRocketSharp />}
|
leftSection={<IoRocketSharp />}
|
||||||
onClick={() => setVisible("premium")(true)}
|
onClick={() => {
|
||||||
|
setVisible("premium")(true);
|
||||||
|
gaEvent("Account Modal", "click upgrade premium");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Upgrade to Premium
|
Upgrade to Premium
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -30,6 +30,7 @@ import { FaTrash } from "react-icons/fa";
|
|||||||
import { SlOptionsVertical } from "react-icons/sl";
|
import { SlOptionsVertical } from "react-icons/sl";
|
||||||
import { VscAdd } from "react-icons/vsc";
|
import { VscAdd } from "react-icons/vsc";
|
||||||
import { FileFormat } from "src/enums/file.enum";
|
import { FileFormat } from "src/enums/file.enum";
|
||||||
|
import { gaEvent } from "src/lib/utils/gaEvent";
|
||||||
import { documentSvc } from "src/services/document.service";
|
import { documentSvc } from "src/services/document.service";
|
||||||
import useFile, { File } from "src/store/useFile";
|
import useFile, { File } from "src/store/useFile";
|
||||||
|
|
||||||
@ -124,6 +125,7 @@ export const CloudModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
|||||||
if (error) throw new Error(error.message);
|
if (error) throw new Error(error.message);
|
||||||
|
|
||||||
if (data[0]) setFile(data[0]);
|
if (data[0]) setFile(data[0]);
|
||||||
|
gaEvent("Cloud Modal", "open file");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) toast.error(error.message);
|
if (error instanceof Error) toast.error(error.message);
|
||||||
} finally {
|
} finally {
|
||||||
@ -134,14 +136,23 @@ export const CloudModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const onDeleteClick = React.useCallback(
|
const onDeleteClick = React.useCallback(
|
||||||
(file: File) => {
|
async (file: File) => {
|
||||||
toast
|
try {
|
||||||
.promise(documentSvc.delete(file.id), {
|
toast.loading("Deleting file...", { id: "delete-file" });
|
||||||
loading: "Deleting file...",
|
|
||||||
error: "An error occurred while deleting the file!",
|
const { error } = await documentSvc.delete(file.id);
|
||||||
success: `Deleted ${file.name}!`,
|
if (error) throw new Error(error.message);
|
||||||
})
|
|
||||||
.then(() => refetch());
|
await refetch();
|
||||||
|
toast.success(`Deleted ${file.name}!`, { id: "delete-file" });
|
||||||
|
gaEvent("Cloud Modal", "delete file");
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
toast.error(error.message, { id: "delete-file" });
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
toast.dismiss("delete-file");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[refetch]
|
[refetch]
|
||||||
);
|
);
|
||||||
|
@ -90,7 +90,7 @@ export const DownloadModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
toast.success("Copied to clipboard");
|
toast.success("Copied to clipboard");
|
||||||
gaEvent("click", "clipboard image");
|
gaEvent("Download Modal", "clipboard image");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("Failed to copy to clipboard");
|
toast.error("Failed to copy to clipboard");
|
||||||
} finally {
|
} finally {
|
||||||
@ -111,7 +111,7 @@ export const DownloadModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
downloadURI(dataURI, `${fileDetails.filename}.${extension}`);
|
downloadURI(dataURI, `${fileDetails.filename}.${extension}`);
|
||||||
gaEvent("download", "download graph image", extension);
|
gaEvent("Download Modal", "download image", extension);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("Failed to download image!");
|
toast.error("Failed to download image!");
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -14,6 +14,7 @@ import { Dropzone } from "@mantine/dropzone";
|
|||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import { AiOutlineUpload } from "react-icons/ai";
|
import { AiOutlineUpload } from "react-icons/ai";
|
||||||
import { FileFormat } from "src/enums/file.enum";
|
import { FileFormat } from "src/enums/file.enum";
|
||||||
|
import { gaEvent } from "src/lib/utils/gaEvent";
|
||||||
import useFile from "src/store/useFile";
|
import useFile from "src/store/useFile";
|
||||||
|
|
||||||
export const ImportModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
export const ImportModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||||
@ -28,6 +29,8 @@ export const ImportModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
|||||||
setFile(null);
|
setFile(null);
|
||||||
|
|
||||||
toast.loading("Loading...", { id: "toastFetch" });
|
toast.loading("Loading...", { id: "toastFetch" });
|
||||||
|
gaEvent("Import Modal", "fetch url");
|
||||||
|
|
||||||
return fetch(url)
|
return fetch(url)
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(json => {
|
.then(json => {
|
||||||
@ -47,6 +50,8 @@ export const ImportModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
|||||||
setURL("");
|
setURL("");
|
||||||
onClose();
|
onClose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gaEvent("Import Modal", "import file", format);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Modal, Button, ModalProps, Textarea, Divider, Group } from "@mantine/core";
|
import { Modal, Button, ModalProps, Textarea, Divider, Group } from "@mantine/core";
|
||||||
import { decode } from "jsonwebtoken";
|
import { decode } from "jsonwebtoken";
|
||||||
|
import { gaEvent } from "src/lib/utils/gaEvent";
|
||||||
import useFile from "src/store/useFile";
|
import useFile from "src/store/useFile";
|
||||||
|
|
||||||
export const JWTModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
export const JWTModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||||
@ -10,8 +11,9 @@ export const JWTModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
|||||||
const resolve = () => {
|
const resolve = () => {
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
const json = decode(token);
|
const json = decode(token);
|
||||||
|
|
||||||
setContents({ contents: JSON.stringify(json, null, 2) });
|
setContents({ contents: JSON.stringify(json, null, 2) });
|
||||||
|
|
||||||
|
gaEvent("JWT Modal", "resolve");
|
||||||
setToken("");
|
setToken("");
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@ import React from "react";
|
|||||||
import { Modal, Stack, Text, ScrollArea, ModalProps, Button } from "@mantine/core";
|
import { Modal, Stack, Text, ScrollArea, ModalProps, Button } from "@mantine/core";
|
||||||
import { CodeHighlight } from "@mantine/code-highlight";
|
import { CodeHighlight } from "@mantine/code-highlight";
|
||||||
import { VscLock } from "react-icons/vsc";
|
import { VscLock } from "react-icons/vsc";
|
||||||
|
import { gaEvent } from "src/lib/utils/gaEvent";
|
||||||
import useGraph from "src/store/useGraph";
|
import useGraph from "src/store/useGraph";
|
||||||
import useModal from "src/store/useModal";
|
import useModal from "src/store/useModal";
|
||||||
|
|
||||||
@ -32,7 +33,10 @@ export const NodeModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
|||||||
</ScrollArea.Autosize>
|
</ScrollArea.Autosize>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setVisible("premium")(true)}
|
onClick={() => {
|
||||||
|
setVisible("premium")(true);
|
||||||
|
gaEvent("Node Modal", "edit");
|
||||||
|
}}
|
||||||
rightSection={<VscLock strokeWidth={0.5} />}
|
rightSection={<VscLock strokeWidth={0.5} />}
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { BsCheck } from "react-icons/bs";
|
import { BsCheck } from "react-icons/bs";
|
||||||
import { MdChevronRight } from "react-icons/md";
|
import { MdChevronRight } from "react-icons/md";
|
||||||
|
import { gaEvent } from "src/lib/utils/gaEvent";
|
||||||
|
|
||||||
export const PremiumModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
export const PremiumModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||||
return (
|
return (
|
||||||
@ -42,6 +43,7 @@ export const PremiumModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
|||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
<Title order={3}>Premium</Title>
|
<Title order={3}>Premium</Title>
|
||||||
<Button
|
<Button
|
||||||
|
onClick={() => gaEvent("Premium Modal", "click upgrade premium")}
|
||||||
component={Link}
|
component={Link}
|
||||||
prefetch={false}
|
prefetch={false}
|
||||||
href="/pricing"
|
href="/pricing"
|
||||||
|
@ -3,6 +3,7 @@ import { Stack, Modal, Button, ModalProps, Text, Anchor, Group, Divider } from "
|
|||||||
import Editor from "@monaco-editor/react";
|
import Editor from "@monaco-editor/react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { VscLinkExternal } from "react-icons/vsc";
|
import { VscLinkExternal } from "react-icons/vsc";
|
||||||
|
import { gaEvent } from "src/lib/utils/gaEvent";
|
||||||
import useConfig from "src/store/useConfig";
|
import useConfig from "src/store/useConfig";
|
||||||
import useFile from "src/store/useFile";
|
import useFile from "src/store/useFile";
|
||||||
|
|
||||||
@ -32,8 +33,9 @@ export const SchemaModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
|||||||
const onApply = () => {
|
const onApply = () => {
|
||||||
try {
|
try {
|
||||||
const parsedSchema = JSON.parse(schema);
|
const parsedSchema = JSON.parse(schema);
|
||||||
|
|
||||||
setJsonSchema(parsedSchema);
|
setJsonSchema(parsedSchema);
|
||||||
|
|
||||||
|
gaEvent("Schema Modal", "apply");
|
||||||
toast.success("Applied schema!");
|
toast.success("Applied schema!");
|
||||||
onClose();
|
onClose();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { FiExternalLink } from "react-icons/fi";
|
import { FiExternalLink } from "react-icons/fi";
|
||||||
import { MdCheck, MdCopyAll } from "react-icons/md";
|
import { MdCheck, MdCopyAll } from "react-icons/md";
|
||||||
|
import { gaEvent } from "src/lib/utils/gaEvent";
|
||||||
|
|
||||||
export const ShareModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
export const ShareModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||||
const { query } = useRouter();
|
const { query } = useRouter();
|
||||||
@ -32,7 +33,13 @@ export const ShareModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
|||||||
<CopyButton value={shareURL} timeout={2000}>
|
<CopyButton value={shareURL} timeout={2000}>
|
||||||
{({ copied, copy }) => (
|
{({ copied, copy }) => (
|
||||||
<Tooltip label={copied ? "Copied" : "Copy"} withArrow position="right">
|
<Tooltip label={copied ? "Copied" : "Copy"} withArrow position="right">
|
||||||
<ActionIcon color={copied ? "teal" : "gray"} onClick={copy}>
|
<ActionIcon
|
||||||
|
color={copied ? "teal" : "gray"}
|
||||||
|
onClick={() => {
|
||||||
|
copy();
|
||||||
|
gaEvent("Share Modal", "copy");
|
||||||
|
}}
|
||||||
|
>
|
||||||
{copied ? <MdCheck size="1rem" /> : <MdCopyAll size="1rem" />}
|
{copied ? <MdCheck size="1rem" /> : <MdCopyAll size="1rem" />}
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Stack, Modal, ModalProps, Select, ScrollArea } from "@mantine/core";
|
import { Stack, Modal, ModalProps, Select, ScrollArea } from "@mantine/core";
|
||||||
import { CodeHighlight } from "@mantine/code-highlight";
|
import { CodeHighlight } from "@mantine/code-highlight";
|
||||||
|
import { gaEvent } from "src/lib/utils/gaEvent";
|
||||||
import useJson from "src/store/useJson";
|
import useJson from "src/store/useJson";
|
||||||
|
|
||||||
enum Language {
|
enum Language {
|
||||||
@ -93,7 +94,10 @@ export const TypeModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
|||||||
<Select
|
<Select
|
||||||
value={selectedType}
|
value={selectedType}
|
||||||
data={typeOptions}
|
data={typeOptions}
|
||||||
onChange={e => setSelectedType(e as Language)}
|
onChange={e => {
|
||||||
|
setSelectedType(e as Language);
|
||||||
|
gaEvent("Type Modal", "generate", e as string);
|
||||||
|
}}
|
||||||
allowDeselect={false}
|
allowDeselect={false}
|
||||||
/>
|
/>
|
||||||
<ScrollArea.Autosize mah={400} maw={700}>
|
<ScrollArea.Autosize mah={400} maw={700}>
|
||||||
|
@ -19,7 +19,7 @@ export const FileMenu = () => {
|
|||||||
a.download = `jsoncrack.${getFormat()}`;
|
a.download = `jsoncrack.${getFormat()}`;
|
||||||
a.click();
|
a.click();
|
||||||
|
|
||||||
gaEvent("download", "file download");
|
gaEvent("File Menu", "download", getFormat());
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -2,6 +2,7 @@ import React from "react";
|
|||||||
import { Menu, Text, Flex } from "@mantine/core";
|
import { Menu, Text, Flex } from "@mantine/core";
|
||||||
import { BsCheck2 } from "react-icons/bs";
|
import { BsCheck2 } from "react-icons/bs";
|
||||||
import { MdSettings } from "react-icons/md";
|
import { MdSettings } from "react-icons/md";
|
||||||
|
import { gaEvent } from "src/lib/utils/gaEvent";
|
||||||
import useConfig from "src/store/useConfig";
|
import useConfig from "src/store/useConfig";
|
||||||
import * as Styles from "./styles";
|
import * as Styles from "./styles";
|
||||||
|
|
||||||
@ -32,37 +33,55 @@ export const OptionsMenu = () => {
|
|||||||
<Menu.Dropdown>
|
<Menu.Dropdown>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
leftSection={<BsCheck2 opacity={rulersEnabled ? 100 : 0} />}
|
leftSection={<BsCheck2 opacity={rulersEnabled ? 100 : 0} />}
|
||||||
onClick={() => toggleRulers(!rulersEnabled)}
|
onClick={() => {
|
||||||
|
toggleRulers(!rulersEnabled);
|
||||||
|
gaEvent("Options Menu", "toggle rulers", rulersEnabled ? "on" : "off");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Text size="xs">Rulers</Text>
|
<Text size="xs">Rulers</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
leftSection={<BsCheck2 opacity={gesturesEnabled ? 100 : 0} />}
|
leftSection={<BsCheck2 opacity={gesturesEnabled ? 100 : 0} />}
|
||||||
onClick={() => toggleGestures(!gesturesEnabled)}
|
onClick={() => {
|
||||||
|
toggleGestures(!gesturesEnabled);
|
||||||
|
gaEvent("Options Menu", "toggle gestures", gesturesEnabled ? "on" : "off");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Text size="xs">Trackpad Gestures</Text>
|
<Text size="xs">Trackpad Gestures</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
leftSection={<BsCheck2 opacity={childrenCountVisible ? 100 : 0} />}
|
leftSection={<BsCheck2 opacity={childrenCountVisible ? 100 : 0} />}
|
||||||
onClick={() => toggleChildrenCount(!childrenCountVisible)}
|
onClick={() => {
|
||||||
|
toggleChildrenCount(!childrenCountVisible);
|
||||||
|
gaEvent("Options Menu", "toggle children count", childrenCountVisible ? "on" : "off");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Text size="xs">Item Count</Text>
|
<Text size="xs">Item Count</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
leftSection={<BsCheck2 opacity={imagePreviewEnabled ? 100 : 0} />}
|
leftSection={<BsCheck2 opacity={imagePreviewEnabled ? 100 : 0} />}
|
||||||
onClick={() => toggleImagePreview(!imagePreviewEnabled)}
|
onClick={() => {
|
||||||
|
toggleImagePreview(!imagePreviewEnabled);
|
||||||
|
gaEvent("Options Menu", "toggle image preview", imagePreviewEnabled ? "on" : "off");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Text size="xs">Image Link Preview</Text>
|
<Text size="xs">Image Link Preview</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
leftSection={<BsCheck2 opacity={collapseButtonVisible ? 100 : 0} />}
|
leftSection={<BsCheck2 opacity={collapseButtonVisible ? 100 : 0} />}
|
||||||
onClick={() => toggleCollapseButton(!collapseButtonVisible)}
|
onClick={() => {
|
||||||
|
toggleCollapseButton(!collapseButtonVisible);
|
||||||
|
gaEvent("Options Menu", "toggle collapse button", collapseButtonVisible ? "on" : "off");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Text size="xs">Show Expand/Collapse</Text>
|
<Text size="xs">Show Expand/Collapse</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
leftSection={<BsCheck2 opacity={darkmodeEnabled ? 100 : 0} />}
|
leftSection={<BsCheck2 opacity={darkmodeEnabled ? 100 : 0} />}
|
||||||
onClick={() => toggleDarkMode(!darkmodeEnabled)}
|
onClick={() => {
|
||||||
|
toggleDarkMode(!darkmodeEnabled);
|
||||||
|
gaEvent("Options Menu", "toggle dark mode", darkmodeEnabled ? "on" : "off");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Text size="xs">Dark Mode</Text>
|
<Text size="xs">Dark Mode</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
@ -14,7 +14,7 @@ export const ToolsMenu = () => {
|
|||||||
return (
|
return (
|
||||||
<Menu shadow="md" withArrow>
|
<Menu shadow="md" withArrow>
|
||||||
<Menu.Target>
|
<Menu.Target>
|
||||||
<Styles.StyledToolElement onClick={() => gaEvent("click", "tools menu")}>
|
<Styles.StyledToolElement onClick={() => gaEvent("Tools Menu", "toggle menu")}>
|
||||||
<Flex align="center" gap={3}>
|
<Flex align="center" gap={3}>
|
||||||
Tools <CgChevronDown />
|
Tools <CgChevronDown />
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -25,27 +25,50 @@ export const ToolsMenu = () => {
|
|||||||
fz={12}
|
fz={12}
|
||||||
leftSection={<MdCompare />}
|
leftSection={<MdCompare />}
|
||||||
rightSection={<VscLock />}
|
rightSection={<VscLock />}
|
||||||
onClick={() => setVisible("premium")(true)}
|
onClick={() => {
|
||||||
|
setVisible("premium")(true);
|
||||||
|
gaEvent("Tools Menu", "open", "Compare Data");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Compare Data
|
Compare Data
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item fz={12} leftSection={<VscSearchFuzzy />} onClick={() => setVisible("jq")(true)}>
|
<Menu.Item
|
||||||
|
fz={12}
|
||||||
|
leftSection={<VscSearchFuzzy />}
|
||||||
|
onClick={() => {
|
||||||
|
setVisible("jq")(true);
|
||||||
|
gaEvent("Tools Menu", "open", "JSON Query");
|
||||||
|
}}
|
||||||
|
>
|
||||||
JSON Query (jq)
|
JSON Query (jq)
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item fz={12} leftSection={<VscJson />} onClick={() => setVisible("schema")(true)}>
|
<Menu.Item
|
||||||
|
fz={12}
|
||||||
|
leftSection={<VscJson />}
|
||||||
|
onClick={() => {
|
||||||
|
setVisible("schema")(true);
|
||||||
|
gaEvent("Tools Menu", "open", "JSON Schema");
|
||||||
|
}}
|
||||||
|
>
|
||||||
JSON Schema
|
JSON Schema
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
fz={12}
|
fz={12}
|
||||||
leftSection={<SiJsonwebtokens />}
|
leftSection={<SiJsonwebtokens />}
|
||||||
onClick={() => setVisible("jwt")(true)}
|
onClick={() => {
|
||||||
|
setVisible("jwt")(true);
|
||||||
|
gaEvent("Tools Menu", "open", "Decode JWT");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Decode JWT
|
Decode JWT
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
fz={12}
|
fz={12}
|
||||||
leftSection={<VscGroupByRefType />}
|
leftSection={<VscGroupByRefType />}
|
||||||
onClick={() => setVisible("type")(true)}
|
onClick={() => {
|
||||||
|
setVisible("type")(true);
|
||||||
|
gaEvent("Tools Menu", "open", "Generate Type");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Generate Type
|
Generate Type
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
@ -65,7 +65,7 @@ export const ViewMenu = () => {
|
|||||||
return (
|
return (
|
||||||
<Menu shadow="md" closeOnItemClick={false} withArrow>
|
<Menu shadow="md" closeOnItemClick={false} withArrow>
|
||||||
<Menu.Target>
|
<Menu.Target>
|
||||||
<Styles.StyledToolElement onClick={() => gaEvent("click", "view menu")}>
|
<Styles.StyledToolElement onClick={() => gaEvent("View Menu", "open menu")}>
|
||||||
<Flex align="center" gap={3}>
|
<Flex align="center" gap={3}>
|
||||||
View <CgChevronDown />
|
View <CgChevronDown />
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -76,7 +76,10 @@ export const ViewMenu = () => {
|
|||||||
miw={205}
|
miw={205}
|
||||||
size="xs"
|
size="xs"
|
||||||
value={viewMode}
|
value={viewMode}
|
||||||
onChange={e => setViewMode(e as ViewMode)}
|
onChange={e => {
|
||||||
|
setViewMode(e as ViewMode);
|
||||||
|
gaEvent("View Menu", "change view mode", e as string);
|
||||||
|
}}
|
||||||
data={[
|
data={[
|
||||||
{ value: ViewMode.Graph, label: "Graph" },
|
{ value: ViewMode.Graph, label: "Graph" },
|
||||||
{ value: ViewMode.Tree, label: "Tree" },
|
{ value: ViewMode.Tree, label: "Tree" },
|
||||||
@ -88,7 +91,10 @@ export const ViewMenu = () => {
|
|||||||
<Menu.Item
|
<Menu.Item
|
||||||
mt="xs"
|
mt="xs"
|
||||||
fz={12}
|
fz={12}
|
||||||
onClick={toggleDirection}
|
onClick={() => {
|
||||||
|
toggleDirection();
|
||||||
|
gaEvent("View Menu", "rotate layout");
|
||||||
|
}}
|
||||||
leftSection={<Styles.StyledFlowIcon rotate={rotateLayout(direction || "RIGHT")} />}
|
leftSection={<Styles.StyledFlowIcon rotate={rotateLayout(direction || "RIGHT")} />}
|
||||||
rightSection={
|
rightSection={
|
||||||
<Text ml="md" fz={10} c="dimmed">
|
<Text ml="md" fz={10} c="dimmed">
|
||||||
@ -100,7 +106,10 @@ export const ViewMenu = () => {
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
fz={12}
|
fz={12}
|
||||||
onClick={toggleExpandCollapseGraph}
|
onClick={() => {
|
||||||
|
toggleExpandCollapseGraph();
|
||||||
|
gaEvent("View Menu", "expand collapse graph");
|
||||||
|
}}
|
||||||
leftSection={graphCollapsed ? <VscExpandAll /> : <VscCollapseAll />}
|
leftSection={graphCollapsed ? <VscExpandAll /> : <VscCollapseAll />}
|
||||||
rightSection={
|
rightSection={
|
||||||
<Text ml="md" fz={10} c="dimmed">
|
<Text ml="md" fz={10} c="dimmed">
|
||||||
|
@ -27,7 +27,7 @@ export const ZoomMenu = () => {
|
|||||||
return (
|
return (
|
||||||
<Menu shadow="md" trigger="click" closeOnItemClick={false} withArrow>
|
<Menu shadow="md" trigger="click" closeOnItemClick={false} withArrow>
|
||||||
<Menu.Target>
|
<Menu.Target>
|
||||||
<Styles.StyledToolElement onClick={() => gaEvent("click", "zoom menu")}>
|
<Styles.StyledToolElement onClick={() => gaEvent("Zoom Menu", "open menu")}>
|
||||||
<Flex gap={4} align="center">
|
<Flex gap={4} align="center">
|
||||||
{Math.round(zoomFactor * 100)}%
|
{Math.round(zoomFactor * 100)}%
|
||||||
<CgChevronDown />
|
<CgChevronDown />
|
||||||
@ -45,22 +45,56 @@ export const ZoomMenu = () => {
|
|||||||
rightSection="%"
|
rightSection="%"
|
||||||
/>
|
/>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item rightSection="+" onClick={zoomIn}>
|
<Menu.Item
|
||||||
|
rightSection="+"
|
||||||
|
onClick={() => {
|
||||||
|
zoomIn();
|
||||||
|
gaEvent("Zoom Menu", "zoom in");
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Text size="xs">Zoom in</Text>
|
<Text size="xs">Zoom in</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item rightSection="-" onClick={zoomOut}>
|
<Menu.Item
|
||||||
|
rightSection="-"
|
||||||
|
onClick={() => {
|
||||||
|
zoomOut();
|
||||||
|
gaEvent("Zoom Menu", "zoom out");
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Text size="xs">Zoom out</Text>
|
<Text size="xs">Zoom out</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item rightSection="⇧ 1" onClick={centerView}>
|
<Menu.Item
|
||||||
|
rightSection="⇧ 1"
|
||||||
|
onClick={() => {
|
||||||
|
centerView();
|
||||||
|
gaEvent("Zoom Menu", "center view");
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Text size="xs">Zoom to fit</Text>
|
<Text size="xs">Zoom to fit</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item onClick={() => setZoomFactor(50 / 100)}>
|
<Menu.Item
|
||||||
|
onClick={() => {
|
||||||
|
setZoomFactor(50 / 100);
|
||||||
|
gaEvent("Zoom Menu", "zoom to 50%");
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Text size="xs">Zoom to %50</Text>
|
<Text size="xs">Zoom to %50</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item rightSection="⇧ 0" onClick={() => setZoomFactor(100 / 100)}>
|
<Menu.Item
|
||||||
|
rightSection="⇧ 0"
|
||||||
|
onClick={() => {
|
||||||
|
setZoomFactor(100 / 100);
|
||||||
|
gaEvent("Zoom Menu", "zoom to 100%");
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Text size="xs">Zoom to %100</Text>
|
<Text size="xs">Zoom to %100</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item onClick={() => setZoomFactor(200 / 100)}>
|
<Menu.Item
|
||||||
|
onClick={() => {
|
||||||
|
setZoomFactor(200 / 100);
|
||||||
|
gaEvent("Zoom Menu", "zoom to 200%");
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Text size="xs">Zoom to %200</Text>
|
<Text size="xs">Zoom to %200</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu.Dropdown>
|
</Menu.Dropdown>
|
||||||
|
@ -8,6 +8,7 @@ import { FiDownload } from "react-icons/fi";
|
|||||||
import { SearchInput } from "src/components/SearchInput";
|
import { SearchInput } from "src/components/SearchInput";
|
||||||
import { FileFormat } from "src/enums/file.enum";
|
import { FileFormat } from "src/enums/file.enum";
|
||||||
import { JSONCrackLogo } from "src/layout/JsonCrackLogo";
|
import { JSONCrackLogo } from "src/layout/JsonCrackLogo";
|
||||||
|
import { gaEvent } from "src/lib/utils/gaEvent";
|
||||||
import useFile from "src/store/useFile";
|
import useFile from "src/store/useFile";
|
||||||
import useModal from "src/store/useModal";
|
import useModal from "src/store/useModal";
|
||||||
import { AccountMenu } from "./AccountMenu";
|
import { AccountMenu } from "./AccountMenu";
|
||||||
@ -80,6 +81,7 @@ export const Toolbar: React.FC<{ isWidget?: boolean }> = ({ isWidget = false })
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSeenPremium(true);
|
setSeenPremium(true);
|
||||||
setVisible("premium")(true);
|
setVisible("premium")(true);
|
||||||
|
gaEvent("Toolbar", "click upgrade premium");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Indicator
|
<Indicator
|
||||||
|
@ -4,6 +4,7 @@ import { Button, Title } from "@mantine/core";
|
|||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { MdChevronRight } from "react-icons/md";
|
import { MdChevronRight } from "react-icons/md";
|
||||||
import { JSONCrackLogo } from "src/layout/JsonCrackLogo";
|
import { JSONCrackLogo } from "src/layout/JsonCrackLogo";
|
||||||
|
import { gaEvent } from "src/lib/utils/gaEvent";
|
||||||
|
|
||||||
const StyledPremiumView = styled.div`
|
const StyledPremiumView = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -166,6 +167,7 @@ export const PremiumView = () => (
|
|||||||
</StyledInfo>
|
</StyledInfo>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
onClick={() => gaEvent("Premium View", "click upgrade premium")}
|
||||||
component={Link}
|
component={Link}
|
||||||
prefetch={false}
|
prefetch={false}
|
||||||
href="/pricing"
|
href="/pricing"
|
||||||
|
@ -39,7 +39,7 @@ export const useFocusNode = () => {
|
|||||||
setNodeCount(0);
|
setNodeCount(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
gaEvent("input", "search node in graph");
|
gaEvent("Graph", "search");
|
||||||
}, [selectedNode, debouncedValue, value, viewPort]);
|
}, [selectedNode, debouncedValue, value, viewPort]);
|
||||||
|
|
||||||
return [value, setValue, skip, nodeCount, selectedNode] as const;
|
return [value, setValue, skip, nodeCount, selectedNode] as const;
|
||||||
|
@ -15,6 +15,7 @@ import styled from "styled-components";
|
|||||||
import { AiOutlineInfoCircle } from "react-icons/ai";
|
import { AiOutlineInfoCircle } from "react-icons/ai";
|
||||||
import { VscArrowRight } from "react-icons/vsc";
|
import { VscArrowRight } from "react-icons/vsc";
|
||||||
import Layout from "src/layout/Layout";
|
import Layout from "src/layout/Layout";
|
||||||
|
import { gaEvent } from "src/lib/utils/gaEvent";
|
||||||
import useUser from "src/store/useUser";
|
import useUser from "src/store/useUser";
|
||||||
|
|
||||||
const purchaseLinks = {
|
const purchaseLinks = {
|
||||||
@ -161,6 +162,7 @@ export const PricingCards = () => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
<Button
|
<Button
|
||||||
component="a"
|
component="a"
|
||||||
|
onClick={() => gaEvent("Pricing", "click upgrade premium")}
|
||||||
href={paymentURL(isMonthly ? purchaseLinks.monthly : purchaseLinks.annual)}
|
href={paymentURL(isMonthly ? purchaseLinks.monthly : purchaseLinks.annual)}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
size="lg"
|
size="lg"
|
||||||
|
@ -45,6 +45,6 @@ export const documentSvc = {
|
|||||||
return await supabase.from("document").update(data).eq("id", id).select("private");
|
return await supabase.from("document").update(data).eq("id", id).select("private");
|
||||||
},
|
},
|
||||||
delete: async (id: string) => {
|
delete: async (id: string) => {
|
||||||
await supabase.from("document").delete().eq("id", id);
|
return await supabase.from("document").delete().eq("id", id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,6 @@ import { toast } from "react-hot-toast";
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { defaultJson } from "src/constants/data";
|
import { defaultJson } from "src/constants/data";
|
||||||
import { FileFormat } from "src/enums/file.enum";
|
import { FileFormat } from "src/enums/file.enum";
|
||||||
import { gaEvent } from "src/lib/utils/gaEvent";
|
|
||||||
import { contentToJson, jsonToContent } from "src/lib/utils/json/jsonAdapter";
|
import { contentToJson, jsonToContent } from "src/lib/utils/json/jsonAdapter";
|
||||||
import { isIframe } from "src/lib/utils/widget";
|
import { isIframe } from "src/lib/utils/widget";
|
||||||
import { documentSvc } from "src/services/document.service";
|
import { documentSvc } from "src/services/document.service";
|
||||||
@ -69,19 +68,6 @@ const debouncedUpdateJson = debounce((value: unknown) => {
|
|||||||
useJson.getState().setJson(JSON.stringify(value, null, 2));
|
useJson.getState().setJson(JSON.stringify(value, null, 2));
|
||||||
}, 800);
|
}, 800);
|
||||||
|
|
||||||
const filterArrayAndObjectFields = (obj: object) => {
|
|
||||||
const result = {};
|
|
||||||
|
|
||||||
for (const key in obj) {
|
|
||||||
if (obj.hasOwnProperty(key)) {
|
|
||||||
if (Array.isArray(obj[key]) || typeof obj[key] === "object") {
|
|
||||||
result[key] = obj[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
const useFile = create<FileStates & JsonActions>()((set, get) => ({
|
const useFile = create<FileStates & JsonActions>()((set, get) => ({
|
||||||
...initialStates,
|
...initialStates,
|
||||||
clear: () => {
|
clear: () => {
|
||||||
@ -105,7 +91,6 @@ const useFile = create<FileStates & JsonActions>()((set, get) => ({
|
|||||||
const jsonContent = await jsonToContent(JSON.stringify(contentJson, null, 2), format);
|
const jsonContent = await jsonToContent(JSON.stringify(contentJson, null, 2), format);
|
||||||
|
|
||||||
get().setContents({ contents: jsonContent });
|
get().setContents({ contents: jsonContent });
|
||||||
gaEvent("input", "file format change");
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
get().clear();
|
get().clear();
|
||||||
console.warn("The content was unable to be converted, so it was cleared instead.");
|
console.warn("The content was unable to be converted, so it was cleared instead.");
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { Modal } from "src/containers/Modals";
|
import { Modal } from "src/containers/Modals";
|
||||||
import { gaEvent } from "src/lib/utils/gaEvent";
|
|
||||||
import useUser from "./useUser";
|
import useUser from "./useUser";
|
||||||
|
|
||||||
type ModalState = {
|
type ModalState = {
|
||||||
@ -40,7 +39,6 @@ const useModal = create<ModalState & ModalActions>()(set => ({
|
|||||||
return set({ login: true });
|
return set({ login: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (visible) gaEvent("modal", `open ${modal}`);
|
|
||||||
set({ [modal]: visible });
|
set({ [modal]: visible });
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user