feat: upgrade mantine v7 & ui changes

This commit is contained in:
AykutSarac 2023-12-24 13:59:30 +03:00
parent ecb825e5e0
commit 9133475ce1
No known key found for this signature in database
62 changed files with 5753 additions and 6513 deletions

View File

@ -26,9 +26,9 @@ jobs:
- name: Detect package manager - name: Detect package manager
id: detect-package-manager id: detect-package-manager
run: | run: |
echo "manager=yarn" >> $GITHUB_OUTPUT echo "manager=pnpm" >> $GITHUB_OUTPUT
echo "command=install --frozen-lockfile" >> $GITHUB_OUTPUT echo "command=install --frozen-lockfile" >> $GITHUB_OUTPUT
echo "runner=yarn" >> $GITHUB_OUTPUT echo "runner=pnpm" >> $GITHUB_OUTPUT
exit 0 exit 0
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
@ -40,8 +40,8 @@ jobs:
with: with:
path: | path: |
~/.npm ~/.npm
~/.cache/yarn ~/.cache/pnpm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }} key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json', '**/pnpm-lock.yaml') }}
restore-keys: ${{ runner.os }}-node- restore-keys: ${{ runner.os }}-node-
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v3 uses: actions/configure-pages@v3
@ -52,9 +52,9 @@ jobs:
with: with:
path: | path: |
.next/cache .next/cache
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/pnpm-lock.yaml') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
restore-keys: | restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}- ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/pnpm-lock.yaml') }}-
- name: Install dependencies - name: Install dependencies
run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}
- name: Build with Next.js - name: Build with Next.js

View File

@ -18,7 +18,7 @@ jobs:
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
cache: 'yarn' cache: 'pnpm'
- run: yarn install - run: pnpm install
- run: yarn lint - run: pnpm lint
- run: yarn build - run: pnpm build

2
.gitignore vendored
View File

@ -21,8 +21,6 @@
# debug # debug
npm-debug.log* npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files # local env files
.env.local .env.local

View File

@ -7,6 +7,8 @@
"importOrder": [ "importOrder": [
"^(react/(.*)$)|^(react$)", "^(react/(.*)$)|^(react$)",
"^(next/(.*)$)|^(next$)", "^(next/(.*)$)|^(next$)",
"^@mantine/core",
"^@mantine",
"styled", "styled",
"<THIRD_PARTY_MODULES>", "<THIRD_PARTY_MODULES>",
"^src/(.*)$", "^src/(.*)$",

View File

@ -23,13 +23,13 @@ git clone https://github.com/AykutSarac/jsoncrack.com.git
After cloning the repository, you can install the required dependencies by running the following command: After cloning the repository, you can install the required dependencies by running the following command:
```bash ```bash
yarn install pnpm install
``` ```
To run the development server, you can run the following command: To run the development server, you can run the following command:
```bash ```bash
yarn dev pnpm dev
``` ```
## Contributing Guidelines ## Contributing Guidelines

View File

@ -3,12 +3,12 @@ FROM node:18-alpine as builder
WORKDIR /src WORKDIR /src
# Cache dependencies first # Cache dependencies first
COPY package.json yarn.lock ./ COPY package.json pnpm-lock.yaml ./
RUN yarn install --frozen-lockfile RUN pnpm install
# Copy other files and build # Copy other files and build
COPY . /src/ COPY . /src/
RUN yarn build RUN pnpm build
# App # App
FROM nginxinc/nginx-unprivileged FROM nginxinc/nginx-unprivileged

View File

@ -58,11 +58,11 @@ If you like the project, you can become a sponsor at [GitHub Sponsors](https://g
After cloning the repository, run the following commands: After cloning the repository, run the following commands:
```console ```console
# Install the packages # Install the packages
yarn install pnpm install
# Start development server # Start development server
# Then the development server will run at http://localhost:3000 # Then the development server will run at http://localhost:3000
yarn dev pnpm dev
``` ```
### Docker ### Docker

View File

@ -14,17 +14,14 @@
"analyze": "ANALYZE=true npm run build" "analyze": "ANALYZE=true npm run build"
}, },
"dependencies": { "dependencies": {
"@emotion/react": "^11.11.1", "@mantine/code-highlight": "^7.3.2",
"@emotion/server": "^11.11.0", "@mantine/core": "^7.3.2",
"@mantine/core": "^6.0.17", "@mantine/hooks": "^7.3.2",
"@mantine/hooks": "^6.0.17",
"@mantine/next": "^6.0.21",
"@mantine/prism": "^6.0.21",
"@monaco-editor/react": "^4.5.2", "@monaco-editor/react": "^4.5.2",
"@sentry/nextjs": "^7.72.0", "@sentry/nextjs": "^7.91.0",
"@supabase/auth-helpers-nextjs": "^0.8.1", "@supabase/auth-helpers-nextjs": "^0.8.1",
"@supabase/auth-helpers-react": "^0.4.2", "@supabase/auth-helpers-react": "^0.4.2",
"@supabase/supabase-js": "^2.36.0", "@supabase/supabase-js": "^2.39.1",
"@tanstack/react-query": "^4.35.3", "@tanstack/react-query": "^4.35.3",
"allotment": "^1.19.3", "allotment": "^1.19.3",
"axios": "^1.5.0", "axios": "^1.5.0",
@ -42,7 +39,7 @@
"lodash.get": "^4.4.2", "lodash.get": "^4.4.2",
"lodash.set": "^4.3.2", "lodash.set": "^4.3.2",
"maketypes": "^1.1.2", "maketypes": "^1.1.2",
"next": "13.4.12", "next": "14.0.4",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-ga4": "^2.1.0", "react-ga4": "^2.1.0",
@ -53,7 +50,7 @@
"react-simple-typewriter": "^5.0.1", "react-simple-typewriter": "^5.0.1",
"react-zoomable-ui": "^0.11.0", "react-zoomable-ui": "^0.11.0",
"reaflow": "5.2.8", "reaflow": "5.2.8",
"styled-components": "^6.1.1", "styled-components": "^6.1.3",
"toml": "^3.0.0", "toml": "^3.0.0",
"use-long-press": "^3.1.5", "use-long-press": "^3.1.5",
"zustand": "^4.4.7" "zustand": "^4.4.7"
@ -67,14 +64,14 @@
"@types/lodash.get": "^4.4.9", "@types/lodash.get": "^4.4.9",
"@types/lodash.set": "^4.3.9", "@types/lodash.set": "^4.3.9",
"@types/node": "^20.4.7", "@types/node": "^20.4.7",
"@types/react": "18.2.18", "@types/react": "18.2.45",
"eslint": "8.56.0", "eslint": "8.56.0",
"eslint-config-next": "13.4.12", "eslint-config-next": "14.0.4",
"eslint-config-prettier": "^8.10.0", "eslint-config-prettier": "^8.10.0",
"eslint-plugin-prettier": "^5.0.0", "eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-unused-imports": "^3.0.0",
"prettier": "^3.1.1", "prettier": "^3.1.1",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "5.1.6" "typescript": "5.3.3"
} }
} }

5010
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +0,0 @@
import React from "react";
import { useRouter } from "next/router";
export const HovercardAds = () => {
const router = useRouter();
React.useEffect(() => {
const isAvailable = document.querySelectorAll("#_hellobar_")[0];
if (typeof window._bsa !== "undefined" && window._bsa && !!!isAvailable) {
window._bsa.init("hellobar", "CE7IPKQL", "placement:jsoncrackcom");
}
if (isAvailable) {
window._bsa.reload("#_hellobar_");
}
}, [router.asPath]);
return <div id="hellobarContainer"></div>;
};

View File

@ -19,10 +19,10 @@ export const SearchInput: React.FC = () => {
onFocus={() => ReactGA.event({ action: "focus_node_search", category: "User" })} onFocus={() => ReactGA.event({ action: "focus_node_search", category: "User" })}
placeholder="Search Node" placeholder="Search Node"
onKeyDown={getHotkeyHandler([["Enter", skip]])} onKeyDown={getHotkeyHandler([["Enter", skip]])}
icon={<AiOutlineSearch />} leftSection={<AiOutlineSearch />}
rightSection={ rightSection={
<Flex h={30} align="center" gap="sm"> <Flex h={30} align="center" gap="sm">
<Text size="xs" color="dimmed"> <Text size="xs" c="dimmed">
{searchValue && `${nodeCount}/${nodeCount > 0 ? currentNode + 1 : "0"}`} {searchValue && `${nodeCount}/${nodeCount > 0 ? currentNode + 1 : "0"}`}
</Text> </Text>
</Flex> </Flex>

View File

@ -2,34 +2,24 @@ import { createGlobalStyle } from "styled-components";
const GlobalStyle = createGlobalStyle` const GlobalStyle = createGlobalStyle`
html, body { html, body {
color: ${({ theme }) => theme.FULL_WHITE} !important; background: rgb(246,249,253);
font-weight: 400; background: radial-gradient(circle, rgb(245 245 245) 33%, rgb(252 252 255) 100%);
font-size: 16px;
background: rgb(246,249,253) !important;
background: radial-gradient(circle, rgb(245 245 245) 33%, rgb(252 252 255) 100%) !important;
@media only screen and (max-width: 768px) {
background-position: right;
}
} }
* { *,
-webkit-tap-highlight-color: transparent; *::before,
scroll-behavior: smooth !important; *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
scroll-behavior: smooth !important;
-webkit-tap-highlight-color: transparent;
} }
.hide { .hide {
display: none; display: none;
} }
/* g {
opacity: 1 !important;
} */
.mantine-Modal-inner {
padding: 0;
}
svg { svg {
vertical-align: text-top; vertical-align: text-top;
} }
@ -48,92 +38,6 @@ const GlobalStyle = createGlobalStyle`
padding: 0; padding: 0;
cursor: pointer; cursor: pointer;
} }
#carbonads * {
margin: initial;
padding: initial;
line-height: initial;
}
#carbonads {
--carbon-font-size: 16px;
--carbon-padding-size: 12px;
}
#carbonads {
width: 100%;
z-index: 100;
display: block;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI',
Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue',
Helvetica, Arial, sans-serif;
font-size: var(--carbon-font-size);
}
#carbonads > span {
min-width: 18.75em;
min-height: 100px;
background-color: hsl(0, 0%, 10%);
box-shadow: 0 0 1px hsl(0deg 0% 0% / 0.085),
0 0 2px hsl(0deg 0% 0% / 0.085),
0 0 4px hsl(0deg 0% 0% / 0.085),
0 0 8px hsl(0deg 0% 0% / 0.085);
}
#carbonads a {
text-decoration: none;
color: #ddd;
}
#carbonads a:hover {
color: #ddd;
}
#carbonads span {
display: block;
position: relative;
}
#carbonads .carbon-wrap {
display: flex;
}
#carbonads .carbon-img {
height: 100px;
width: 130px;
}
#carbonads .carbon-img img {
display: block;
}
#carbonads .carbon-text {
padding: 0.625em 1em;
font-size: 0.8125em;
margin-bottom: 1em;
line-height: 1.4;
text-align: left;
}
#carbonads .carbon-poweredby {
display: block;
padding: 6px 8px;
color: #aaa;
background: #1e2021;
text-align: center;
text-transform: uppercase;
letter-spacing: 0.1ch;
font-weight: 600;
font-size: 0.5em;
line-height: 1;
border-top-left-radius: 3px;
position: absolute;
bottom: 0;
right: 0;
}
`; `;
export default GlobalStyle; export default GlobalStyle;

View File

@ -83,15 +83,15 @@ export const darkTheme = {
BACKGROUND_NODE: "#2B2C3E", BACKGROUND_NODE: "#2B2C3E",
BACKGROUND_TERTIARY: "#202225", BACKGROUND_TERTIARY: "#202225",
BACKGROUND_SECONDARY: "#2f3136", BACKGROUND_SECONDARY: "#2f3136",
TOOLBAR_BG: "#2f3136", TOOLBAR_BG: "#262626",
BACKGROUND_PRIMARY: "#36393f", BACKGROUND_PRIMARY: "#36393f",
BACKGROUND_MODIFIER_ACCENT: "rgba(79,84,92,0.48)", BACKGROUND_MODIFIER_ACCENT: "rgba(79,84,92,0.48)",
MODAL_BACKGROUND: "#36393E", MODAL_BACKGROUND: "#36393E",
TEXT_NORMAL: "#dcddde", TEXT_NORMAL: "#dcddde",
TEXT_POSITIVE: "hsl(139,calc(var(--saturation-factor, 1)*51.6%),52.2%)", TEXT_POSITIVE: "hsl(139,calc(var(--saturation-factor, 1)*51.6%),52.2%)",
GRID_BG_COLOR: "#1C1C1C", GRID_BG_COLOR: "#1E1E1E",
GRID_COLOR_PRIMARY: "#2A2A2A", GRID_COLOR_PRIMARY: "#272626",
GRID_COLOR_SECONDARY: "#252525", GRID_COLOR_SECONDARY: "#232323",
}; };
export const lightTheme = { export const lightTheme = {
@ -110,7 +110,7 @@ export const lightTheme = {
BACKGROUND_NODE: "#F6F8FA", BACKGROUND_NODE: "#F6F8FA",
BACKGROUND_TERTIARY: "#e3e5e8", BACKGROUND_TERTIARY: "#e3e5e8",
BACKGROUND_SECONDARY: "#f2f3f5", BACKGROUND_SECONDARY: "#f2f3f5",
TOOLBAR_BG: "#FAFAFA", TOOLBAR_BG: "#ECECEC",
BACKGROUND_PRIMARY: "#FFFFFF", BACKGROUND_PRIMARY: "#FFFFFF",
BACKGROUND_MODIFIER_ACCENT: "rgba(106,116,128,0.24)", BACKGROUND_MODIFIER_ACCENT: "rgba(106,116,128,0.24)",
MODAL_BACKGROUND: "#FFFFFF", MODAL_BACKGROUND: "#FFFFFF",

View File

@ -28,7 +28,7 @@ const StyledBottomBar = styled.div`
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
border-top: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT}; border-top: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT};
background: ${({ theme }) => theme.BACKGROUND_TERTIARY}; background: ${({ theme }) => theme.TOOLBAR_BG};
max-height: 27px; max-height: 27px;
height: 27px; height: 27px;
z-index: 35; z-index: 35;
@ -204,19 +204,23 @@ export const BottomBar = () => {
<Popover.Target> <Popover.Target>
<Flex align="center" gap={2}> <Flex align="center" gap={2}>
<VscError color="red" size={16} /> <VscError color="red" size={16} />
<Text color="red" fw="bold"> <Text c="red" fw={500} fz="xs">
Invalid Invalid
</Text> </Text>
</Flex> </Flex>
</Popover.Target> </Popover.Target>
<Popover.Dropdown sx={{ pointerEvents: "none" }}> <Popover.Dropdown
style={{
pointerEvents: "none",
}}
>
<Text size="xs">{error}</Text> <Text size="xs">{error}</Text>
</Popover.Dropdown> </Popover.Dropdown>
</Popover> </Popover>
) : ( ) : (
<Flex align="center" gap={2}> <Flex align="center" gap={2}>
<MdOutlineCheckCircleOutline /> <MdOutlineCheckCircleOutline />
<Text>Valid</Text> <Text size="xs">Valid</Text>
</Flex> </Flex>
)} )}
</StyledBottomBarItem> </StyledBottomBarItem>
@ -242,12 +246,12 @@ export const BottomBar = () => {
{liveTransformEnabled ? ( {liveTransformEnabled ? (
<StyledBottomBarItem onClick={() => toggleLiveTransform(false)}> <StyledBottomBarItem onClick={() => toggleLiveTransform(false)}>
<VscSync /> <VscSync />
<Text>Live Transform</Text> <Text fz="xs">Live Transform</Text>
</StyledBottomBarItem> </StyledBottomBarItem>
) : ( ) : (
<StyledBottomBarItem onClick={() => toggleLiveTransform(true)}> <StyledBottomBarItem onClick={() => toggleLiveTransform(true)}>
<VscSyncIgnored /> <VscSyncIgnored />
<Text>Manual Transform</Text> <Text fz="xs">Manual Transform</Text>
</StyledBottomBarItem> </StyledBottomBarItem>
)} )}
{!liveTransformEnabled && ( {!liveTransformEnabled && (

View File

@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import styled from "styled-components"; import styled from "styled-components";
import { ActionIcon, Input, Loader, Tooltip } from "@mantine/core"; import { ActionIcon, TextInput, Loader, Tooltip } from "@mantine/core";
import { getHotkeyHandler, useSessionStorage } from "@mantine/hooks"; import { getHotkeyHandler, useSessionStorage } from "@mantine/hooks";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { GoDependabot } from "react-icons/go"; import { GoDependabot } from "react-icons/go";
@ -23,7 +23,7 @@ function removeWhitespaces(inputString: string) {
return compactString; return compactString;
} }
const StyledPromptInput = styled(Input)` const StyledPromptInput = styled(TextInput)`
.mantine-Input-input { .mantine-Input-input {
font-weight: 500; font-weight: 500;
background: ${({ theme }) => theme.PROMPT_BG}; background: ${({ theme }) => theme.PROMPT_BG};
@ -112,7 +112,7 @@ const PromptInput = () => {
</ActionIcon> </ActionIcon>
</> </>
} }
icon={completing ? <Loader size="xs" /> : <GoDependabot strokeWidth={1} />} leftSection={completing ? <Loader size="xs" /> : <GoDependabot strokeWidth={1} />}
radius={0} radius={0}
/> />
</div> </div>

View File

@ -1,153 +0,0 @@
import {
createStyles,
Title,
Text,
Card,
SimpleGrid,
Container,
rem,
MediaQuery,
} from "@mantine/core";
import { FaProjectDiagram, FaRegImages } from "react-icons/fa";
import { MdOutlineDesignServices } from "react-icons/md";
import { TbTransform } from "react-icons/tb";
import { VscCloud, VscJson } from "react-icons/vsc";
const mockdata = [
{
title: "Designed for Everyone",
description:
"We focus on simplicity and ease of use. Whether you're a data scientist or product owner, JSON Crack is the tool for you to understand your data.",
icon: <MdOutlineDesignServices size="30" />,
},
{
title: "Interactive Graphs",
description:
"JSON Crack visualizes data from JSON, YAML, XML, CSV, and more in interactive graphs. Users can click nodes for more details, edit data, and search through the graph.",
icon: <FaProjectDiagram size="30" />,
hideMobile: true,
},
{
title: "Multi-Format Support & Data Conversion",
description:
"JSON Crack seamlessly handles various data formats, allowing easy conversion between them.",
icon: <TbTransform size="30" />,
hideMobile: true,
},
{
title: "Cloud Storage and Sharing",
description:
"JSON Crack comes with built-in cloud storage, allowing users to store their data and easily share links with collaborators or other stakeholders, fostering efficient teamwork and collaboration.",
icon: <VscCloud size="30" />,
hideMobile: true,
},
{
title: "Graph Export & Download",
description:
"Users have the option to download the generated graph as an image, enabling them to save and share visual representations of their data with others or use them in presentations and reports.",
icon: <FaRegImages size="30" />,
},
{
title: "JSON Schema Support",
description:
"JSON Crack supports JSON Schema, offering users the ability to validate and enforce data structure rules, ensuring data consistency and integrity.",
icon: <VscJson size="30" />,
},
];
const useStyles = createStyles(theme => ({
title: {
fontSize: rem(34),
fontWeight: 900,
[theme.fn.smallerThan("sm")]: {
fontSize: rem(24),
},
},
description: {
maxWidth: 600,
margin: "auto",
"&::after": {
content: '""',
display: "block",
backgroundColor: theme.fn.primaryColor(),
width: rem(45),
height: rem(2),
marginTop: theme.spacing.sm,
marginLeft: "auto",
marginRight: "auto",
},
},
card: {
border: `${rem(1)} solid ${
theme.colorScheme === "dark" ? theme.colors.dark[5] : theme.colors.gray[1]
}`,
},
cardTitle: {
"&::after": {
content: '""',
display: "block",
backgroundColor: theme.fn.primaryColor(),
width: rem(45),
height: rem(2),
marginTop: theme.spacing.sm,
},
},
}));
export function FeaturesCards() {
const { classes } = useStyles();
const features = mockdata.map(feature => (
<MediaQuery
key={feature.title}
query="(max-width: 36em)"
styles={{ display: feature.hideMobile ? "none" : "initial" }}
>
<Card shadow="md" radius="md" className={classes.card} padding="xl">
{feature.icon}
<Text fz="lg" fw={500} className={classes.cardTitle} mt="md">
{feature.title}
</Text>
<Text fz="sm" color="dark.3" mt="sm">
{feature.description}
</Text>
</Card>
</MediaQuery>
));
return (
<Container size="lg" py="xl" mt={40}>
<Title
variant="gradient"
gradient={{ from: "purple", to: "orange", deg: 45 }}
order={2}
className={classes.title}
ta="center"
mt="sm"
>
Transform data into stunning graphs.
</Title>
<Text color="dark.3" className={classes.description} ta="center" mt="md">
JSON Crack empowers users to visualize, analyze, and manipulate data with ease, making it a
versatile and powerful tool for data representation and exploration.
</Text>
<SimpleGrid
cols={3}
spacing="xl"
mt={50}
breakpoints={[
{ maxWidth: "sm", cols: 1 },
{ maxWidth: "md", cols: 2 },
]}
>
{features}
</SimpleGrid>
</Container>
);
}

View File

@ -27,7 +27,7 @@ export const AccountModal: React.FC<ModalProps> = ({ opened, onClose }) => {
return ( return (
<Modal title={`Hello, ${user?.user_metadata.name}!`} opened={opened} onClose={onClose} centered> <Modal title={`Hello, ${user?.user_metadata.name}!`} opened={opened} onClose={onClose} centered>
<Paper p="md"> <Paper p="md">
<Group noWrap> <Group>
<Avatar src={user?.user_metadata.avatar_url} size={94}> <Avatar src={user?.user_metadata.avatar_url} size={94}>
JC JC
</Avatar> </Avatar>
@ -36,13 +36,13 @@ export const AccountModal: React.FC<ModalProps> = ({ opened, onClose }) => {
{user?.user_metadata.name} {user?.user_metadata.name}
</Text> </Text>
<Group noWrap spacing={10} mt={3}> <Group gap={10} mt={3}>
<Text fz="xs" c="dimmed"> <Text fz="xs" c="dimmed">
{user?.email} {user?.email}
</Text> </Text>
</Group> </Group>
<Group noWrap spacing={10} mt={5}> <Group gap={10} mt={5}>
<Text fz="xs" c="dimmed"> <Text fz="xs" c="dimmed">
<Badge <Badge
size="sm" size="sm"
@ -60,7 +60,7 @@ export const AccountModal: React.FC<ModalProps> = ({ opened, onClose }) => {
</Paper> </Paper>
<Divider py="xs" /> <Divider py="xs" />
<Group position="right"> <Group justify="right">
{isPremium && !premiumCancelled ? ( {isPremium && !premiumCancelled ? (
<Button <Button
variant="light" variant="light"
@ -77,7 +77,7 @@ export const AccountModal: React.FC<ModalProps> = ({ opened, onClose }) => {
<Button <Button
variant="gradient" variant="gradient"
gradient={{ from: "teal", to: "lime", deg: 105 }} gradient={{ from: "teal", to: "lime", deg: 105 }}
leftIcon={<IoRocketSharp />} leftSection={<IoRocketSharp />}
onClick={() => setVisible("premium")(true)} onClick={() => setVisible("premium")(true)}
> >
UPGRADE TO PREMIUM! UPGRADE TO PREMIUM!

View File

@ -44,22 +44,22 @@ export const CancelPremiumModal: React.FC<ModalProps> = ({ opened, onClose }) =>
return ( return (
<Modal title="CANCEL PREMIUM?" opened={opened} onClose={onClose} centered> <Modal title="CANCEL PREMIUM?" opened={opened} onClose={onClose} centered>
<Image py="xs" src="assets/taken.svg" mx="auto" width={200} alt="taken" /> <Image py="xs" src="assets/taken.svg" mx="auto" w={200} alt="taken" />
<Text fz="sm" pb="md"> <Text fz="sm" pb="md">
Cancellation will take effect at the end of your current billing period. Cancellation will take effect at the end of your current billing period.
<br /> <br />
<br /> <br />
You can restart your subscription anytime. You can restart your subscription anytime.
<br /> <br />
<Text size="xs" color="dimmed"> <Anchor fz="xs" target="_blank" href="https://patreon.com/herowand">
If you have problems with cancelling plan please contact: contact@jsoncrack.com
</Text>
<Anchor target="_blank" href="https://patreon.com/herowand">
Click here to cancel if you are Patreon member Click here to cancel if you are Patreon member
</Anchor> </Anchor>
<Text size="xs" c="dimmed" mt="lg">
If you have problems with cancelling plan please contact: contact@jsoncrack.com
</Text>
</Text> </Text>
<Divider py="xs" /> <Divider py="xs" />
<Group position="right"> <Group justify="right">
<Button color="dark" variant="subtle" onClick={onClose}> <Button color="dark" variant="subtle" onClick={onClose}>
Cancel Cancel
</Button> </Button>

View File

@ -24,7 +24,7 @@ export const ClearModal: React.FC<ModalProps> = ({ opened, onClose }) => {
<Text>Are you sure you want to delete JSON?</Text> <Text>Are you sure you want to delete JSON?</Text>
</Group> </Group>
<Divider py="xs" /> <Divider py="xs" />
<Group position="right"> <Group justify="right">
<Button color="red" onClick={handleClear}> <Button color="red" onClick={handleClear}>
Confirm Confirm
</Button> </Button>

View File

@ -74,7 +74,7 @@ const UpdateNameModal: React.FC<{
placeholder={file?.name} placeholder={file?.name}
onChange={e => setName(e.currentTarget.value)} onChange={e => setName(e.currentTarget.value)}
/> />
<Group position="right"> <Group justify="right">
<Button variant="outline" onClick={onClose}> <Button variant="outline" onClick={onClose}>
Cancel Cancel
</Button> </Button>
@ -144,48 +144,63 @@ export const CloudModal: React.FC<ModalProps> = ({ opened, onClose }) => {
const rows = React.useMemo( const rows = React.useMemo(
() => () =>
data?.map(element => ( data?.map(element => (
<tr key={element.id}> <Table.Tr key={element.id}>
<td> <Table.Td>
<Flex align="center" gap="xs"> <Flex align="center" gap="xs">
{element.private ? <VscLock /> : <VscUnlock />} {element.private ? <VscLock /> : <VscUnlock />}
{element.id} {element.id}
</Flex> </Flex>
</td> </Table.Td>
<td> <Table.Td>
<Flex align="center" justify="space-between"> <Flex align="center" justify="space-between">
{element.name} {element.name}
<ActionIcon color="cyan" onClick={() => setCurrentFile(element)}> <ActionIcon
variant="transparent"
color="cyan"
onClick={() => setCurrentFile(element)}
>
<VscEdit /> <VscEdit />
</ActionIcon> </ActionIcon>
</Flex> </Flex>
</td> </Table.Td>
<td>{dayjs(element.created_at).format("DD.MM.YYYY")}</td> <Table.Td>{dayjs(element.created_at).format("DD.MM.YYYY")}</Table.Td>
<td> <Table.Td>
<Badge variant="light" color={colorByFormat[element.format]} size="sm"> <Badge variant="light" color={colorByFormat[element.format]} size="sm">
{element.format.toUpperCase()} {element.format.toUpperCase()}
</Badge> </Badge>
</td> </Table.Td>
<td>{element.views.toLocaleString("en-US")}</td> <Table.Td>{element.views.toLocaleString("en-US")}</Table.Td>
<td> <Table.Td>
<Flex gap="xs"> <Flex gap="xs">
<ActionIcon <ActionIcon.Group>
component={Link} <ActionIcon
href={`?json=${element.id}`} variant="transparent"
prefetch={false} component={Link}
color="blue" href={`?json=${element.id}`}
onClick={onClose} prefetch={false}
> color="blue"
<MdFileOpen size="18" /> onClick={onClose}
</ActionIcon> >
<ActionIcon color="red" onClick={() => onDeleteClick(element)}> <MdFileOpen size="18" />
<FaTrash size="18" /> </ActionIcon>
</ActionIcon> <ActionIcon
<ActionIcon color="green" onClick={() => copyShareLink(element.id)}> variant="transparent"
<AiOutlineLink /> color="red"
</ActionIcon> onClick={() => onDeleteClick(element)}
>
<FaTrash size="18" />
</ActionIcon>
<ActionIcon
variant="transparent"
color="green"
onClick={() => copyShareLink(element.id)}
>
<AiOutlineLink />
</ActionIcon>
</ActionIcon.Group>
</Flex> </Flex>
</td> </Table.Td>
</tr> </Table.Tr>
)), )),
[data, copyShareLink, onClose, onDeleteClick] [data, copyShareLink, onClose, onDeleteClick]
); );
@ -214,10 +229,10 @@ export const CloudModal: React.FC<ModalProps> = ({ opened, onClose }) => {
]} ]}
/> />
<div> <div>
<Text color="dimmed" size="xs" transform="uppercase" weight={700}> <Text c="dimmed" fz="xs" fw={700} style={{ textTransform: "uppercase" }}>
Total Quota Total Quota
</Text> </Text>
<Text weight={700} size="lg"> <Text fw={700} size="lg">
{data.length} / {totalQuota} {data.length} / {totalQuota}
</Text> </Text>
</div> </div>
@ -231,11 +246,19 @@ export const CloudModal: React.FC<ModalProps> = ({ opened, onClose }) => {
onClick={onCreate} onClick={onCreate}
disabled={isCreateDisabled} disabled={isCreateDisabled}
> >
<Text fz="md" align="center" color="blue"> <Text
<Flex align="center" justify="center"> fz="sm"
<VscAdd size="24" /> fw="bold"
Create New Document c="blue"
</Flex> style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
textAlign: "center",
}}
>
<VscAdd size="18" strokeWidth={1} />
Create New Document
</Text> </Text>
</UnstyledButton> </UnstyledButton>
</Paper> </Paper>
@ -248,18 +271,18 @@ export const CloudModal: React.FC<ModalProps> = ({ opened, onClose }) => {
<Divider py="xs" /> <Divider py="xs" />
<Paper> <Paper>
<ScrollArea h="100%" offsetScrollbars> <ScrollArea h="100%" offsetScrollbars>
<Table fontSize="xs" verticalSpacing="xs" striped withBorder> <Table fz="xs" verticalSpacing="xs" striped withTableBorder>
<thead> <Table.Thead>
<tr> <Table.Tr>
<th>ID</th> <Table.Th>ID</Table.Th>
<th>Name</th> <Table.Th>Name</Table.Th>
<th>Create Date</th> <Table.Th>Create Date</Table.Th>
<th>Format</th> <Table.Th>Format</Table.Th>
<th>Views</th> <Table.Th>Views</Table.Th>
<th>Actions</th> <Table.Th>Actions</Table.Th>
</tr> </Table.Tr>
</thead> </Table.Thead>
<tbody>{rows}</tbody> <Table.Tbody>{rows}</Table.Tbody>
</Table> </Table>
</ScrollArea> </ScrollArea>
</Paper> </Paper>

View File

@ -7,10 +7,8 @@ import {
Modal, Modal,
Button, Button,
Divider, Divider,
Grid,
ModalProps, ModalProps,
ColorInput, ColorInput,
Stack,
} from "@mantine/core"; } from "@mantine/core";
import { toBlob, toJpeg, toPng, toSvg } from "html-to-image"; import { toBlob, toJpeg, toPng, toSvg } from "html-to-image";
import ReactGA from "react-ga4"; import ReactGA from "react-ga4";
@ -127,49 +125,44 @@ export const DownloadModal: React.FC<ModalProps> = ({ opened, onClose }) => {
return ( return (
<Modal opened={opened} onClose={onClose} title="Download Image" centered> <Modal opened={opened} onClose={onClose} title="Download Image" centered>
<Grid py="sm" align="end" grow> <TextInput
<Grid.Col span={8}> label="File Name"
<TextInput value={fileDetails.filename}
label="File Name" onChange={e => updateDetails("filename", e.target.value)}
value={fileDetails.filename} mb="lg"
onChange={e => updateDetails("filename", e.target.value)} />
/> <SegmentedControl
</Grid.Col> value={extension}
<Grid.Col span={4}> onChange={e => setExtension(e as Extensions)}
<SegmentedControl fullWidth
value={extension} data={[
onChange={e => setExtension(e as Extensions)} { label: "SVG", value: Extensions.SVG },
fullWidth { label: "PNG", value: Extensions.PNG },
data={[ { label: "JPEG", value: Extensions.JPEG },
{ label: "SVG", value: Extensions.SVG }, ]}
{ label: "PNG", value: Extensions.PNG }, mb="lg"
{ label: "JPEG", value: Extensions.JPEG }, />
]} <ColorInput
/> label="Background Color"
</Grid.Col> value={fileDetails.backgroundColor}
</Grid> onChange={color => updateDetails("backgroundColor", color)}
<Stack py="sm"> withEyeDropper={false}
<ColorInput mb="lg"
label="Background Color" />
value={fileDetails.backgroundColor} <ColorPicker
onChange={color => updateDetails("backgroundColor", color)} format="rgba"
withEyeDropper={false} value={fileDetails.backgroundColor}
/> onChange={color => updateDetails("backgroundColor", color)}
<ColorPicker swatches={swatches}
format="rgba" withPicker={false}
value={fileDetails.backgroundColor} fullWidth
onChange={color => updateDetails("backgroundColor", color)} />
swatches={swatches} <Divider my="lg" />
withPicker={false} <Group justify="right">
fullWidth <Button leftSection={<FiCopy />} onClick={clipboardImage}>
/>
</Stack>
<Divider py="xs" />
<Group position="right">
<Button leftIcon={<FiCopy />} onClick={clipboardImage}>
Clipboard Clipboard
</Button> </Button>
<Button color="green" leftIcon={<FiDownload />} onClick={exportAsImage}> <Button color="green" leftSection={<FiDownload />} onClick={exportAsImage}>
Download Download
</Button> </Button>
</Group> </Group>

View File

@ -10,7 +10,7 @@ const StyledUploadWrapper = styled.label`
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
background: ${({ theme }) => theme.BACKGROUND_SECONDARY}; background: ${({ theme }) => theme.GRID_BG_COLOR};
border: 2px dashed ${({ theme }) => theme.BACKGROUND_TERTIARY}; border: 2px dashed ${({ theme }) => theme.BACKGROUND_TERTIARY};
border-radius: 5px; border-radius: 5px;
min-height: 200px; min-height: 200px;
@ -102,7 +102,7 @@ export const ImportModal: React.FC<ModalProps> = ({ opened, onClose }) => {
</StyledUploadWrapper> </StyledUploadWrapper>
</Stack> </Stack>
<Divider py="xs" /> <Divider py="xs" />
<Group position="right"> <Group justify="right">
<Button onClick={handleImportFile} disabled={!(jsonFile || url)}> <Button onClick={handleImportFile} disabled={!(jsonFile || url)}>
Import Import
</Button> </Button>

View File

@ -39,7 +39,7 @@ export const JQModal: React.FC<ModalProps> = ({ opened, onClose }) => {
}, },
}} }}
/> />
<Group position="right"> <Group justify="right">
<Button onClick={onApply}>Display on Graph</Button> <Button onClick={onApply}>Display on Graph</Button>
</Group> </Group>
</Stack> </Stack>

View File

@ -1,9 +1,7 @@
import React from "react"; import React from "react";
import { CodeHighlight } from "@mantine/code-highlight";
import { Modal, Stack, Text, ScrollArea, ModalProps, Button } from "@mantine/core"; import { Modal, Stack, Text, ScrollArea, ModalProps, Button } from "@mantine/core";
import { Prism } from "@mantine/prism";
import Editor from "@monaco-editor/react"; import Editor from "@monaco-editor/react";
import vsDark from "prism-react-renderer/themes/vsDark";
import vsLight from "prism-react-renderer/themes/vsLight";
import { VscLock } from "react-icons/vsc"; import { VscLock } from "react-icons/vsc";
import { isIframe } from "src/lib/utils/widget"; import { isIframe } from "src/lib/utils/widget";
import useConfig from "src/store/useConfig"; import useConfig from "src/store/useConfig";
@ -22,36 +20,13 @@ const dataToString = (data: any) => {
return JSON.stringify(text, replacer, 2); return JSON.stringify(text, replacer, 2);
}; };
const CodeBlock: React.FC<{ children: any; [key: string]: any }> = ({
format,
children,
...props
}) => {
return (
<ScrollArea>
<Prism
miw={350}
mah={250}
language="json"
copyLabel="Copy to clipboard"
copiedLabel="Copied to clipboard"
withLineNumbers
getPrismTheme={(_theme, colorScheme) => (colorScheme === "light" ? vsLight : vsDark)}
{...props}
>
{children}
</Prism>
</ScrollArea>
);
};
export const NodeModal: React.FC<ModalProps> = ({ opened, onClose }) => { export const NodeModal: React.FC<ModalProps> = ({ opened, onClose }) => {
const isPremium = useUser(state => state.premium); const isPremium = useUser(state => state.premium);
const editContents = useFile(state => state.editContents); const editContents = useFile(state => state.editContents);
const setVisible = useModal(state => state.setVisible); const setVisible = useModal(state => state.setVisible);
const darkmodeEnabled = useConfig(state => (state.darkmodeEnabled ? "vs-dark" : "light")); const darkmodeEnabled = useConfig(state => (state.darkmodeEnabled ? "vs-dark" : "light"));
const nodeData = useGraph(state => dataToString(state.selectedNode?.text)); const nodeData = useGraph(state => dataToString(state.selectedNode?.text));
const path = useGraph(state => state.selectedNode?.path); const path = useGraph(state => state.selectedNode?.path || "");
const isParent = useGraph(state => state.selectedNode?.data?.isParent); const isParent = useGraph(state => state.selectedNode?.data?.isParent);
const [editMode, setEditMode] = React.useState(false); const [editMode, setEditMode] = React.useState(false);
const [value, setValue] = React.useState(nodeData || ""); const [value, setValue] = React.useState(nodeData || "");
@ -83,8 +58,8 @@ export const NodeModal: React.FC<ModalProps> = ({ opened, onClose }) => {
return ( return (
<Modal title="Node Content" size="auto" opened={opened} onClose={onModalClose} centered> <Modal title="Node Content" size="auto" opened={opened} onClose={onModalClose} centered>
<Stack py="sm" spacing="sm"> <Stack py="sm" gap="sm">
<Stack spacing="xs"> <Stack gap="xs">
<Text fz="sm" fw={700}> <Text fz="sm" fw={700}>
Content Content
</Text> </Text>
@ -103,11 +78,20 @@ export const NodeModal: React.FC<ModalProps> = ({ opened, onClose }) => {
}} }}
/> />
) : ( ) : (
<CodeBlock maw={600}>{nodeData}</CodeBlock> <ScrollArea>
<CodeHighlight
code={nodeData}
miw={350}
mah={250}
maw={600}
language="json"
withCopyButton
/>
</ScrollArea>
)} )}
</Stack> </Stack>
{isEditVisible && ( {isEditVisible && (
<Stack spacing="xs"> <Stack gap="xs">
{editMode ? ( {editMode ? (
<Button <Button
variant={value ? "filled" : "light"} variant={value ? "filled" : "light"}
@ -117,7 +101,11 @@ export const NodeModal: React.FC<ModalProps> = ({ opened, onClose }) => {
{value.length ? "Update Document" : "Cancel"} {value.length ? "Update Document" : "Cancel"}
</Button> </Button>
) : ( ) : (
<Button onClick={onEditClick} leftIcon={!isPremium && <VscLock />} variant="filled"> <Button
onClick={onEditClick}
leftSection={!isPremium && <VscLock />}
variant="filled"
>
Edit Edit
</Button> </Button>
)} )}
@ -126,7 +114,17 @@ export const NodeModal: React.FC<ModalProps> = ({ opened, onClose }) => {
<Text fz="sm" fw={700}> <Text fz="sm" fw={700}>
Node Path Node Path
</Text> </Text>
<CodeBlock>{path}</CodeBlock> <ScrollArea>
<CodeHighlight
code={path}
miw={350}
mah={250}
language="json"
copyLabel="Copy to clipboard"
copiedLabel="Copied to clipboard"
withCopyButton
/>
</ScrollArea>
</Stack> </Stack>
</Modal> </Modal>
); );

View File

@ -17,10 +17,10 @@ export const PremiumModal: React.FC<ModalProps> = ({ opened, onClose }) => {
return ( return (
<Modal title="Your Plan" size="auto" opened={opened} onClose={onClose} centered zIndex={202}> <Modal title="Your Plan" size="auto" opened={opened} onClose={onClose} centered zIndex={202}>
<Flex gap="lg"> <Flex gap="lg">
<Stack spacing="xs"> <Stack gap="xs">
<Title order={3}> <Title order={3}>
Free plan Free plan
<Text size="sm" color="dimmed"> <Text size="sm" c="dimmed">
(Free) (Free)
</Text> </Text>
</Title> </Title>
@ -42,10 +42,10 @@ export const PremiumModal: React.FC<ModalProps> = ({ opened, onClose }) => {
</List> </List>
</Stack> </Stack>
<Divider color="gray" orientation="vertical" /> <Divider color="gray" orientation="vertical" />
<Stack spacing="xs"> <Stack gap="xs">
<Title order={3}> <Title order={3}>
JSON Crack Plus JSON Crack Plus
<Text size="sm" color="dimmed"> <Text size="sm" c="dimmed">
USD 7$/mo USD 7$/mo
</Text> </Text>
</Title> </Title>

View File

@ -32,7 +32,7 @@ export const ReviewModal: React.FC<ModalProps> = ({ opened, onClose }) => {
onClose(); onClose();
}} }}
> >
<Text align="center">How was your experience?</Text> <Text style={{ textAlign: "center" }}>How was your experience?</Text>
<Rating value={stars} onChange={setStars} my="lg" size="xl" mx="auto" /> <Rating value={stars} onChange={setStars} my="lg" size="xl" mx="auto" />
<Textarea <Textarea
placeholder="Please provide feedback on how we can enhance the product and let us know which features you require." placeholder="Please provide feedback on how we can enhance the product and let us know which features you require."
@ -42,10 +42,10 @@ export const ReviewModal: React.FC<ModalProps> = ({ opened, onClose }) => {
maxLength={500} maxLength={500}
minRows={5} minRows={5}
/> />
<Text align="right" size={12} color="dimmed"> <Text fz={12} c="dimmed" style={{ textAlign: "right" }}>
500/{review.length} 500/{review.length}
</Text> </Text>
<Text size={12}> <Text fz={12}>
* Your feedback is kept anonymous. If you wish to be contacted, please provide your email * Your feedback is kept anonymous. If you wish to be contacted, please provide your email
address along with your feedback. address along with your feedback.
</Text> </Text>

View File

@ -59,11 +59,11 @@ export const SchemaModal: React.FC<ModalProps> = ({ opened, onClose }) => {
}, },
}} }}
/> />
<Group position="right"> <Group justify="right">
<Button variant="outline" onClick={onClear} disabled={!schema}> <Button variant="outline" onClick={onClear} disabled={!schema}>
Clear Clear
</Button> </Button>
<Button onClick={onApply} disabled={!schema} rightIcon={!isPremium && <VscLock />}> <Button onClick={onApply} disabled={!schema} rightSection={!isPremium && <VscLock />}>
Apply Apply
</Button> </Button>
</Group> </Group>

View File

@ -48,7 +48,7 @@ export const ShareModal: React.FC<ModalProps> = ({ opened, onClose }) => {
color="green" color="green"
target="_blank" target="_blank"
href="/docs" href="/docs"
leftIcon={<FiExternalLink />} leftSection={<FiExternalLink />}
fullWidth fullWidth
> >
Learn How to Embed Learn How to Embed

View File

@ -1,8 +1,6 @@
import React from "react"; import React from "react";
import { CodeHighlight } from "@mantine/code-highlight";
import { Stack, Modal, ModalProps, ScrollArea, Select } from "@mantine/core"; import { Stack, Modal, ModalProps, ScrollArea, Select } from "@mantine/core";
import { Prism } from "@mantine/prism";
import vsDark from "prism-react-renderer/themes/vsDark";
import vsLight from "prism-react-renderer/themes/vsLight";
import useJson from "src/store/useJson"; import useJson from "src/store/useJson";
enum Language { enum Language {
@ -53,17 +51,14 @@ export const TypeModal: React.FC<ModalProps> = ({ opened, onClose }) => {
onChange={e => setSelectedType(e as Language)} onChange={e => setSelectedType(e as Language)}
/> />
<ScrollArea> <ScrollArea>
<Prism <CodeHighlight
miw={350} miw={350}
mah={600} mah={600}
language={selectedType} language={selectedType}
copyLabel="Copy to clipboard" copyLabel="Copy to clipboard"
copiedLabel="Copied to clipboard" copiedLabel="Copied to clipboard"
withLineNumbers code={type}
getPrismTheme={(_theme, colorScheme) => (colorScheme === "light" ? vsLight : vsDark)} />
>
{type}
</Prism>
</ScrollArea> </ScrollArea>
</Stack> </Stack>
</Modal> </Modal>

View File

@ -32,7 +32,9 @@ export const AccountMenu = () => {
<Menu.Dropdown> <Menu.Dropdown>
{user ? ( {user ? (
<Menu.Item <Menu.Item
icon={<Avatar color="grape" alt={user.user_metadata.name} size={20} radius="xl" />} leftSection={
<Avatar color="grape" alt={user.user_metadata.name} size={20} radius="xl" />
}
onClick={() => setVisible("account")(true)} onClick={() => setVisible("account")(true)}
closeMenuOnClick closeMenuOnClick
> >
@ -40,14 +42,14 @@ export const AccountMenu = () => {
</Menu.Item> </Menu.Item>
) : ( ) : (
<Link href="/sign-in"> <Link href="/sign-in">
<Menu.Item icon={<VscSignIn />}> <Menu.Item leftSection={<VscSignIn />}>
<Text size="xs">Sign in</Text> <Text size="xs">Sign in</Text>
</Menu.Item> </Menu.Item>
</Link> </Link>
)} )}
{!premium && ( {!premium && (
<Menu.Item <Menu.Item
icon={<MdOutlineWorkspacePremium color="red" />} leftSection={<MdOutlineWorkspacePremium color="red" />}
onClick={() => setVisible("premium")(true)} onClick={() => setVisible("premium")(true)}
closeMenuOnClick closeMenuOnClick
> >
@ -60,13 +62,13 @@ export const AccountMenu = () => {
<> <>
<Menu.Divider /> <Menu.Divider />
<Menu.Item <Menu.Item
icon={<VscFeedback />} leftSection={<VscFeedback />}
onClick={() => setVisible("review")(true)} onClick={() => setVisible("review")(true)}
closeMenuOnClick closeMenuOnClick
> >
<Text size="xs">Feedback</Text> <Text size="xs">Feedback</Text>
</Menu.Item> </Menu.Item>
<Menu.Item icon={<VscSignOut />} onClick={() => logout()} closeMenuOnClick> <Menu.Item leftSection={<VscSignOut />} onClick={() => logout()} closeMenuOnClick>
<Text size="xs">Log out</Text> <Text size="xs">Log out</Text>
</Menu.Item> </Menu.Item>
</> </>

View File

@ -5,7 +5,7 @@ import { isIframe } from "src/lib/utils/widget";
import * as Styles from "./styles"; import * as Styles from "./styles";
export const Logo = () => { export const Logo = () => {
const [logoURL, setLogoURL] = React.useState("CTRL"); const [logoURL, setLogoURL] = React.useState("");
React.useEffect(() => { React.useEffect(() => {
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
@ -17,6 +17,8 @@ export const Logo = () => {
} }
}, []); }, []);
if (!logoURL) return null;
return ( return (
<Styles.StyledToolElement title="JSON Crack"> <Styles.StyledToolElement title="JSON Crack">
<Flex gap="xs" align="center" justify="center"> <Flex gap="xs" align="center" justify="center">

View File

@ -31,37 +31,37 @@ export const OptionsMenu = () => {
</Menu.Target> </Menu.Target>
<Menu.Dropdown> <Menu.Dropdown>
<Menu.Item <Menu.Item
icon={<BsCheck2 opacity={rulersEnabled ? 100 : 0} />} leftSection={<BsCheck2 opacity={rulersEnabled ? 100 : 0} />}
onClick={() => toggleRulers(!rulersEnabled)} onClick={() => toggleRulers(!rulersEnabled)}
> >
<Text size="xs">Rulers</Text> <Text size="xs">Rulers</Text>
</Menu.Item> </Menu.Item>
<Menu.Item <Menu.Item
icon={<BsCheck2 opacity={gesturesEnabled ? 100 : 0} />} leftSection={<BsCheck2 opacity={gesturesEnabled ? 100 : 0} />}
onClick={() => toggleGestures(!gesturesEnabled)} onClick={() => toggleGestures(!gesturesEnabled)}
> >
<Text size="xs">Trackpad Gestures</Text> <Text size="xs">Trackpad Gestures</Text>
</Menu.Item> </Menu.Item>
<Menu.Item <Menu.Item
icon={<BsCheck2 opacity={childrenCountVisible ? 100 : 0} />} leftSection={<BsCheck2 opacity={childrenCountVisible ? 100 : 0} />}
onClick={() => toggleChildrenCount(!childrenCountVisible)} onClick={() => toggleChildrenCount(!childrenCountVisible)}
> >
<Text size="xs">Item Count</Text> <Text size="xs">Item Count</Text>
</Menu.Item> </Menu.Item>
<Menu.Item <Menu.Item
icon={<BsCheck2 opacity={imagePreviewEnabled ? 100 : 0} />} leftSection={<BsCheck2 opacity={imagePreviewEnabled ? 100 : 0} />}
onClick={() => toggleImagePreview(!imagePreviewEnabled)} onClick={() => toggleImagePreview(!imagePreviewEnabled)}
> >
<Text size="xs">Image Link Preview</Text> <Text size="xs">Image Link Preview</Text>
</Menu.Item> </Menu.Item>
<Menu.Item <Menu.Item
icon={<BsCheck2 opacity={collapseButtonVisible ? 100 : 0} />} leftSection={<BsCheck2 opacity={collapseButtonVisible ? 100 : 0} />}
onClick={() => toggleCollapseButton(!collapseButtonVisible)} onClick={() => toggleCollapseButton(!collapseButtonVisible)}
> >
<Text size="xs">Show Expand/Collapse</Text> <Text size="xs">Show Expand/Collapse</Text>
</Menu.Item> </Menu.Item>
<Menu.Item <Menu.Item
icon={<BsCheck2 opacity={darkmodeEnabled ? 100 : 0} />} leftSection={<BsCheck2 opacity={darkmodeEnabled ? 100 : 0} />}
onClick={() => toggleDarkMode(!darkmodeEnabled)} onClick={() => toggleDarkMode(!darkmodeEnabled)}
> >
<Text size="xs">Dark Mode</Text> <Text size="xs">Dark Mode</Text>

View File

@ -19,16 +19,24 @@ export const ToolsMenu = () => {
</Styles.StyledToolElement> </Styles.StyledToolElement>
</Menu.Target> </Menu.Target>
<Menu.Dropdown> <Menu.Dropdown>
<Menu.Item fz={12} icon={<VscSearchFuzzy />} onClick={() => setVisible("jq")(true)}> <Menu.Item fz={12} leftSection={<VscSearchFuzzy />} onClick={() => setVisible("jq")(true)}>
JSON Query (jq) JSON Query (jq)
</Menu.Item> </Menu.Item>
<Menu.Item fz={12} icon={<VscJson />} onClick={() => setVisible("schema")(true)}> <Menu.Item fz={12} leftSection={<VscJson />} onClick={() => setVisible("schema")(true)}>
JSON Schema JSON Schema
</Menu.Item> </Menu.Item>
<Menu.Item fz={12} icon={<SiJsonwebtokens />} onClick={() => setVisible("jwt")(true)}> <Menu.Item
fz={12}
leftSection={<SiJsonwebtokens />}
onClick={() => setVisible("jwt")(true)}
>
Decode JWT Decode JWT
</Menu.Item> </Menu.Item>
<Menu.Item fz={12} icon={<VscGroupByRefType />} onClick={() => setVisible("type")(true)}> <Menu.Item
fz={12}
leftSection={<VscGroupByRefType />}
onClick={() => setVisible("type")(true)}
>
Generate Type Generate Type
</Menu.Item> </Menu.Item>
</Menu.Dropdown> </Menu.Dropdown>

View File

@ -86,9 +86,9 @@ export const ViewMenu = () => {
label: "Tools", label: "Tools",
}); });
}} }}
icon={<Styles.StyledFlowIcon rotate={rotateLayout(direction || "RIGHT")} />} leftSection={<Styles.StyledFlowIcon rotate={rotateLayout(direction || "RIGHT")} />}
rightSection={ rightSection={
<Text ml="md" fz={10} color="dimmed"> <Text ml="md" fz={10} c="dimmed">
{coreKey} Shift D {coreKey} Shift D
</Text> </Text>
} }
@ -105,9 +105,9 @@ export const ViewMenu = () => {
label: "Tools", label: "Tools",
}); });
}} }}
icon={foldNodes ? <CgArrowsShrinkH /> : <CgArrowsMergeAltH />} leftSection={foldNodes ? <CgArrowsShrinkH /> : <CgArrowsMergeAltH />}
rightSection={ rightSection={
<Text ml="md" fz={10} color="dimmed"> <Text ml="md" fz={10} c="dimmed">
{coreKey} Shift F {coreKey} Shift F
</Text> </Text>
} }
@ -124,16 +124,16 @@ export const ViewMenu = () => {
label: "Tools", label: "Tools",
}); });
}} }}
icon={graphCollapsed ? <VscExpandAll /> : <VscCollapseAll />} leftSection={graphCollapsed ? <VscExpandAll /> : <VscCollapseAll />}
rightSection={ rightSection={
<Text ml="md" fz={10} color="dimmed"> <Text ml="md" fz={10} c="dimmed">
{coreKey} Shift C {coreKey} Shift C
</Text> </Text>
} }
> >
{graphCollapsed ? "Expand" : "Collapse"} Nodes {graphCollapsed ? "Expand" : "Collapse"} Nodes
</Menu.Item> </Menu.Item>
<Menu.Item fz={12} onClick={focusFirstNode} icon={<VscTarget />}> <Menu.Item fz={12} onClick={focusFirstNode} leftSection={<VscTarget />}>
Focus to First Node Focus to First Node
</Menu.Item> </Menu.Item>
</Menu.Dropdown> </Menu.Dropdown>

View File

@ -21,7 +21,7 @@ export const ViewModeMenu = () => {
<Menu.Dropdown> <Menu.Dropdown>
<SegmentedControl <SegmentedControl
value={viewMode} value={viewMode}
onChange={setViewMode} onChange={e => setViewMode(e as ViewMode)}
data={[ data={[
{ value: ViewMode.Graph, label: "Graph" }, { value: ViewMode.Graph, label: "Graph" },
{ value: ViewMode.Tree, label: "Tree" }, { value: ViewMode.Tree, label: "Tree" },

View File

@ -27,7 +27,7 @@ export const ZoomMenu = () => {
<Menu shadow="md" trigger="click" closeOnItemClick={false} withArrow> <Menu shadow="md" trigger="click" closeOnItemClick={false} withArrow>
<Menu.Target> <Menu.Target>
<Styles.StyledToolElement> <Styles.StyledToolElement>
<Flex gap={4}> <Flex gap={4} align="center">
{Math.round(zoomFactor * 100)}% {Math.round(zoomFactor * 100)}%
<CgChevronDown /> <CgChevronDown />
</Flex> </Flex>

View File

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { Flex, Group, MediaQuery, Select } from "@mantine/core"; import { Flex, Group, Select } from "@mantine/core";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { AiOutlineFullscreen } from "react-icons/ai"; import { AiOutlineFullscreen } from "react-icons/ai";
import { FiDownload } from "react-icons/fi"; import { FiDownload } from "react-icons/fi";
@ -48,49 +48,44 @@ export const Toolbar: React.FC<{ isWidget?: boolean }> = ({ isWidget = false })
<Styles.StyledTools> <Styles.StyledTools>
{isWidget && <Logo />} {isWidget && <Logo />}
{!isWidget && ( {!isWidget && (
<MediaQuery smallerThan="xs" styles={{ display: "none" }}> <Group gap="xs" justify="left" w="100%" style={{ flexWrap: "nowrap" }}>
<Group spacing="xs" position="left" w="100%" noWrap> <Styles.StyledToolElement title="JSON Crack">
<Styles.StyledToolElement title="JSON Crack"> <Flex gap="xs" align="center" justify="center">
<Flex gap="xs" align="center" justify="center"> <JSONCrackLogo fontSize="1.2em" />
<JSONCrackLogo fontSize="1.2em" /> </Flex>
</Flex> </Styles.StyledToolElement>
</Styles.StyledToolElement>
<Select <Select
defaultValue="json" defaultValue="json"
size="xs" size="xs"
value={format} value={format}
onChange={setFormat} onChange={e => setFormat(e as FileFormat)}
miw={80} miw={80}
w={120} w={120}
data={[ data={[
{ value: FileFormat.JSON, label: "JSON" }, { value: FileFormat.JSON, label: "JSON" },
{ value: FileFormat.YAML, label: "YAML" }, { value: FileFormat.YAML, label: "YAML" },
{ value: FileFormat.XML, label: "XML" }, { value: FileFormat.XML, label: "XML" },
{ value: FileFormat.TOML, label: "TOML" }, { value: FileFormat.TOML, label: "TOML" },
{ value: FileFormat.CSV, label: "CSV" }, { value: FileFormat.CSV, label: "CSV" },
]} ]}
/> />
<ViewModeMenu /> <ViewModeMenu />
<Styles.StyledToolElement <Styles.StyledToolElement title="Import File" onClick={() => setVisible("import")(true)}>
title="Import File" Import
onClick={() => setVisible("import")(true)} </Styles.StyledToolElement>
> <ViewMenu />
Import <ToolsMenu />
</Styles.StyledToolElement> <Styles.StyledToolElement title="Cloud" onClick={() => setVisible("cloud")(true)}>
<ViewMenu /> Cloud
<ToolsMenu /> </Styles.StyledToolElement>
<Styles.StyledToolElement title="Cloud" onClick={() => setVisible("cloud")(true)}> <Styles.StyledToolElement title="Download as File" onClick={handleSave}>
Cloud Download
</Styles.StyledToolElement> </Styles.StyledToolElement>
<Styles.StyledToolElement title="Download as File" onClick={handleSave}> </Group>
Download
</Styles.StyledToolElement>
</Group>
</MediaQuery>
)} )}
<Group spacing="xs" position="right" w="100%" noWrap> <Group gap="xs" justify="right" w="100%" style={{ flexWrap: "nowrap" }}>
<SearchInput /> <SearchInput />
{!isWidget && ( {!isWidget && (
<> <>

View File

@ -4,15 +4,16 @@ import { TiFlowMerge } from "react-icons/ti";
export const StyledTools = styled.div` export const StyledTools = styled.div`
position: relative; position: relative;
display: flex; display: flex;
width: 100%;
align-items: center; align-items: center;
gap: 4px; gap: 4px;
justify-content: space-between; justify-content: space-between;
height: 36px; height: 40px;
padding: 4px 8px; padding: 4px 8px;
background: ${({ theme }) => theme.TOOLBAR_BG}; background: ${({ theme }) => theme.TOOLBAR_BG};
color: ${({ theme }) => theme.SILVER}; color: ${({ theme }) => theme.SILVER};
box-shadow: 0 1px 0px ${({ theme }) => theme.BACKGROUND_TERTIARY};
z-index: 36; z-index: 36;
border-bottom: 1px solid ${({ theme }) => theme.SILVER_DARK};
@media only screen and (max-width: 320px) { @media only screen and (max-width: 320px) {
display: none; display: none;

View File

@ -159,7 +159,7 @@ const StyledContent = styled.div`
export const PremiumView = () => ( export const PremiumView = () => (
<StyledPremiumView> <StyledPremiumView>
<StyledContent> <StyledContent>
<Title align="center"> <Title>
<Image width="400" src="assets/icon.png" alt="JSON Crack" /> <Image width="400" src="assets/icon.png" alt="JSON Crack" />
</Title> </Title>
<StyledInfo> <StyledInfo>

View File

@ -1,8 +1,7 @@
import React from "react"; import React from "react";
import { MantineProvider } from "@mantine/core";
import { ThemeProvider } from "styled-components"; import { ThemeProvider } from "styled-components";
import { MantineProvider, MantineThemeOverride } from "@mantine/core";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { monaSans } from "src/constants/fonts";
import { lightTheme, darkTheme } from "src/constants/theme"; import { lightTheme, darkTheme } from "src/constants/theme";
import useConfig from "src/store/useConfig"; import useConfig from "src/store/useConfig";
@ -15,56 +14,14 @@ const queryClient = new QueryClient({
}, },
}); });
const mantineTheme: MantineThemeOverride = { export const EditorWrapper: React.FC<{
fontFamily: monaSans.style.fontFamily, children: React.ReactNode;
headings: { }> = ({ children }) => {
fontFamily: monaSans.style.fontFamily,
},
components: {
Divider: {
styles: () => ({
root: {
borderTopColor: "#4D4D4D !important",
},
}),
},
Modal: {
styles: theme => ({
title: {
fontWeight: 700,
},
header: {
backgroundColor: theme.colorScheme === "dark" ? "#4a4d52" : "#F3F3F3",
},
body: {
backgroundColor: theme.colorScheme === "dark" ? "#4a4d52" : "#F3F3F3",
},
}),
},
Button: {
styles: () => ({
inner: {
fontWeight: 700,
},
}),
},
},
};
export const EditorWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const darkmodeEnabled = useConfig(state => state.darkmodeEnabled); const darkmodeEnabled = useConfig(state => state.darkmodeEnabled);
return ( return (
<ThemeProvider theme={darkmodeEnabled ? darkTheme : lightTheme}> <ThemeProvider theme={darkmodeEnabled ? darkTheme : lightTheme}>
<MantineProvider <MantineProvider forceColorScheme={darkmodeEnabled ? "dark" : "light"}>
theme={{
colorScheme: darkmodeEnabled ? "dark" : "light",
...mantineTheme,
}}
withCSSVariables
withGlobalStyles
withNormalizeCSS
>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider> <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</MantineProvider> </MantineProvider>
</ThemeProvider> </ThemeProvider>

View File

@ -59,7 +59,7 @@ const ExternalMode = () => {
onClick={() => setOpen(true)} onClick={() => setOpen(true)}
color="red" color="red"
variant="subtle" variant="subtle"
leftIcon={<VscCode size="1.2rem" />} leftSection={<VscCode size="1.2rem" />}
> >
External Host External Host
</Button> </Button>
@ -68,15 +68,15 @@ const ExternalMode = () => {
<StyledTitle>Hi! Did you like the editor?</StyledTitle> <StyledTitle>Hi! Did you like the editor?</StyledTitle>
<Text> <Text>
You are currently using the external release of the{" "} You are currently using the external release of the{" "}
<Anchor href="https://jsoncrack.com">JSON Crack</Anchor>. Please consider supporting by <Anchor href="https://jsoncrack.com/pricing">JSON Crack</Anchor>. Please consider
one time or monthly sponsorship supporting by buying premium
</Text> </Text>
</Group> </Group>
<Group pt="lg" position="right"> <Group pt="lg" justify="right">
<Button <Button
onClick={closeModal} onClick={closeModal}
component="a" component="a"
href="https://github.com/sponsors/AykutSarac" href="https://jsoncrack.com/pricing"
target="_blank" target="_blank"
variant="light" variant="light"
color="red" color="red"

View File

@ -1,147 +0,0 @@
import Link from "next/link";
import { createStyles, Text, Container, rem, Anchor } from "@mantine/core";
import { JSONCrackLogo } from "../JsonCrackLogo";
const useStyles = createStyles(theme => ({
footer: {
marginTop: rem(120),
paddingTop: `calc(${theme.spacing.xl} * 2)`,
paddingBottom: `calc(${theme.spacing.xl} * 2)`,
backgroundColor: theme.colorScheme === "dark" ? theme.colors.dark[6] : "white",
borderTop: `${rem(1)} solid ${
theme.colorScheme === "dark" ? theme.colors.dark[5] : theme.colors.gray[2]
}`,
},
logo: {
maxWidth: rem(200),
[theme.fn.smallerThan("sm")]: {
display: "flex",
flexDirection: "column",
alignItems: "center",
},
},
description: {
marginTop: rem(5),
[theme.fn.smallerThan("sm")]: {
marginTop: theme.spacing.xs,
textAlign: "center",
},
},
inner: {
display: "flex",
justifyContent: "space-between",
[theme.fn.smallerThan("sm")]: {
flexDirection: "column",
alignItems: "center",
},
},
groups: {
display: "flex",
flexWrap: "wrap",
[theme.fn.smallerThan("sm")]: {
display: "none",
},
},
wrapper: {
width: rem(160),
},
link: {
display: "block",
color: theme.colorScheme === "dark" ? theme.colors.dark[1] : theme.colors.gray[7],
fontSize: theme.fontSizes.sm,
paddingTop: rem(3),
paddingBottom: rem(3),
"&:hover": {
textDecoration: "underline",
},
},
title: {
fontSize: theme.fontSizes.lg,
fontWeight: 700,
fontFamily: `Greycliff CF, ${theme.fontFamily}`,
marginBottom: `calc(${theme.spacing.xs} / 2)`,
color: theme.colorScheme === "dark" ? theme.white : theme.black,
},
afterFooter: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginTop: theme.spacing.xl,
paddingTop: theme.spacing.xl,
paddingBottom: theme.spacing.xl,
borderTop: `${rem(1)} solid ${
theme.colorScheme === "dark" ? theme.colors.dark[4] : theme.colors.gray[3]
}`,
[theme.fn.smallerThan("sm")]: {
flexDirection: "column",
},
},
social: {
[theme.fn.smallerThan("sm")]: {
marginTop: theme.spacing.xs,
},
},
}));
interface FooterLinksProps {
data: {
title: string;
links: { label: string; link: string }[];
}[];
}
export function FooterLinks({ data }: FooterLinksProps) {
const { classes } = useStyles();
const groups = data.map(group => {
const links = group.links.map((link, index) => (
<Text key={index} className={classes.link} component={Link} href={link.link} prefetch={false}>
{link.label}
</Text>
));
return (
<div className={classes.wrapper} key={group.title}>
<Text className={classes.title}>{group.title}</Text>
{links}
</div>
);
});
return (
<footer className={classes.footer}>
<Container className={classes.inner}>
<div className={classes.logo}>
<JSONCrackLogo />
<Text size="xs" color="gray" className={classes.description}>
Next-generation tools matching with your needs to understand the data.
</Text>
</div>
<div className={classes.groups}>{groups}</div>
</Container>
<Container className={classes.afterFooter}>
<Text color="gray" size="sm">
© jsoncrack.com
</Text>
<Anchor href="mailto:contact@jsoncrack.com" color="gray" size="sm">
contact@jsoncrack.com
</Anchor>
</Container>
</footer>
);
}

View File

@ -1,62 +0,0 @@
import React from "react";
import { FooterLinks } from "./FooterLinks";
export const Footer = () => {
return (
<FooterLinks
data={[
{
title: "Product",
links: [
{
label: "GitHub",
link: "https://github.com/AykutSarac/jsoncrack.com",
},
{
label: "Supporters",
link: "/oss",
},
{
label: "Contributing",
link: "https://github.com/AykutSarac/jsoncrack.com/blob/main/CONTRIBUTING.md",
},
],
},
{
title: "Social",
links: [
{
label: "Discord",
link: "https://discord.gg/yVyTtCRueq",
},
{
label: "𝕏 (Twitter)",
link: "https://twitter.com/jsoncrack",
},
{
label: "LinkedIn",
link: "https://www.linkedin.com/company/herowand",
},
],
},
{
title: "Legal",
links: [
{
label: "Terms",
link: "/legal/terms",
},
{
label: "Privacy",
link: "/legal/privacy",
},
{
label: "Subscription & Refund",
link: "/legal/subscription-refund",
},
],
},
]}
/>
);
};

View File

@ -6,18 +6,11 @@ import { monaSans } from "src/constants/fonts";
const StyledTitle = styled.div<{ fontSize: string }>` const StyledTitle = styled.div<{ fontSize: string }>`
font-weight: 900; font-weight: 900;
margin: 0; margin: 0;
color: ${({ theme }) => theme.INTERACTIVE_HOVER};
font-family: ${monaSans.style.fontFamily}; font-family: ${monaSans.style.fontFamily};
font-size: ${({ fontSize }) => fontSize}; font-size: ${({ fontSize }) => fontSize};
white-space: nowrap; white-space: nowrap;
`; z-index: 10;
color: ${({ theme }) => theme.TEXT_NORMAL};
const StyledGradientText = styled.span`
background: #ffb76b;
background: linear-gradient(to right, #fca74d 0%, #fda436 30%, #ff7c00 60%, #ff7f04 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
`; `;
interface LogoProps extends React.ComponentPropsWithoutRef<"a"> { interface LogoProps extends React.ComponentPropsWithoutRef<"a"> {
@ -26,10 +19,8 @@ interface LogoProps extends React.ComponentPropsWithoutRef<"a"> {
export const JSONCrackLogo: React.FC<LogoProps> = ({ fontSize = "1.2rem", ...props }) => { export const JSONCrackLogo: React.FC<LogoProps> = ({ fontSize = "1.2rem", ...props }) => {
return ( return (
<Link href="/" prefetch={false} {...props}> <StyledTitle as={Link} fontSize={fontSize} href="/" prefetch={false} {...props}>
<StyledTitle fontSize={fontSize}> JSON CRACK
<StyledGradientText>JSON</StyledGradientText> CRACK </StyledTitle>
</StyledTitle>
</Link>
); );
}; };

View File

@ -1,14 +1,23 @@
import React from "react"; import React from "react";
import { Footer } from "../Footer"; import { MantineProvider } from "@mantine/core";
import styled, { ThemeProvider } from "styled-components";
import { lightTheme } from "src/constants/theme";
import { Navbar } from "../Navbar"; import { Navbar } from "../Navbar";
const StyledLayoutWrapper = styled.div`
padding-bottom: 48px;
`;
const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => { const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
return ( return (
<> <MantineProvider forceColorScheme="light">
<Navbar /> <ThemeProvider theme={lightTheme}>
{children} <StyledLayoutWrapper>
<Footer /> <Navbar />
</> {children}
</StyledLayoutWrapper>
</ThemeProvider>
</MantineProvider>
); );
}; };

View File

@ -1,25 +1,26 @@
import React from "react"; import React from "react";
import Link from "next/link"; import Link from "next/link";
import { Button, Menu } from "@mantine/core";
import styled from "styled-components"; import styled from "styled-components";
import { Button } from "@mantine/core"; import { BiChevronDown } from "react-icons/bi";
import useUser from "src/store/useUser"; import useUser from "src/store/useUser";
import { JSONCrackLogo } from "../JsonCrackLogo"; import { JSONCrackLogo } from "../JsonCrackLogo";
const StyledNavbarWrapper = styled.div` const StyledNavbarWrapper = styled.div``;
padding: 10px 0;
`;
const StyledNavbar = styled.div` const StyledNavbar = styled.div`
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
width: 90vw; width: 100%;
height: 56px; height: 56px;
margin: 0 auto; margin: 0 auto;
border: 2px solid black; border-bottom: 1px solid gray;
background: white; background: rgba(255, 255, 255, 0.75);
padding: 8px 16px; padding: 8px 16px;
border-radius: 30px; box-shadow:
0 0 0 1px rgba(0, 0, 0, 0.35),
0 2px 5px 0 rgba(0, 0, 0, 0.35);
@media only screen and (max-width: 1024px) { @media only screen and (max-width: 1024px) {
.desktop { .desktop {
@ -53,14 +54,7 @@ export const Navbar = () => {
<JSONCrackLogo /> <JSONCrackLogo />
</Left> </Left>
<Middle className="hide-mobile"> <Middle className="hide-mobile">
<Button <Button component={Link} href="/pricing" variant="subtle" color="dark" radius="md">
component={Link}
href="/pricing"
prefetch={false}
variant="subtle"
color="dark"
radius="md"
>
Pricing Pricing
</Button> </Button>
<Button <Button
@ -83,6 +77,59 @@ export const Navbar = () => {
> >
Docs Docs
</Button> </Button>
<Menu trigger="hover" offset={15} withArrow>
<Menu.Target>
<Button
variant="subtle"
color="dark"
radius="md"
rightSection={<BiChevronDown size="18" />}
>
Legal
</Button>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item component={Link} href="/legal/privacy" prefetch={false}>
Privacy Policy
</Menu.Item>
<Menu.Item component={Link} href="/legal/terms" prefetch={false}>
Terms and Conditions
</Menu.Item>
<Menu.Item component={Link} href="/legal/subscription-refund" prefetch={false}>
Subscription
</Menu.Item>
<Menu.Divider />
<Menu.Item component="a" href="mailto:contact@jsoncrack.com">
contact@jsoncrack.com
</Menu.Item>
</Menu.Dropdown>
</Menu>
<Menu trigger="hover" offset={15} withArrow>
<Menu.Target>
<Button
variant="subtle"
color="dark"
radius="md"
rightSection={<BiChevronDown size="18" />}
>
Social
</Button>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item component="a" href="https://twitter.com/jsoncrack">
𝕏 (Twitter)
</Menu.Item>
<Menu.Item component="a" href="https://discord.gg/yVyTtCRueq">
Discord
</Menu.Item>
<Menu.Item component="a" href="https://www.linkedin.com/company/herowand">
LinkedIn
</Menu.Item>
<Menu.Item component="a" href="https://github.com/AykutSarac/jsoncrack.com">
GitHub
</Menu.Item>
</Menu.Dropdown>
</Menu>
</Middle> </Middle>
<Right> <Right>
{!isAuthenticated && ( {!isAuthenticated && (
@ -90,14 +137,14 @@ export const Navbar = () => {
component={Link} component={Link}
href="/sign-in" href="/sign-in"
prefetch={false} prefetch={false}
variant="light" variant="outline"
radius="md" color="grape.9"
className="hide-mobile" className="hide-mobile"
> >
Login Login
</Button> </Button>
)} )}
<Button component={Link} href="/editor" prefetch={false} color="pink" radius="md"> <Button color="grape.9" component={Link} href="/editor" prefetch={false}>
Editor Editor
</Button> </Button>
</Right> </Right>

View File

@ -1,33 +0,0 @@
import React from "react";
import styled from "styled-components";
const StyledImage = styled.img`
max-width: 300px;
display: block;
@media only screen and (max-width: 768px) {
max-width: 250px;
}
`;
const StyledProducthuntWrapper = styled.span``;
export const Producthunt = () => {
return (
<StyledProducthuntWrapper>
<a
href="https://www.producthunt.com/posts/json-crack?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-json&#0045;crack"
target="_blank"
rel="noreferrer"
>
<StyledImage
width="300"
height="64"
src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=332281&theme=neutral"
alt="JSON Crack - Simple visualization tool for your JSON data. | Product Hunt"
loading="lazy"
/>
</a>
</StyledProducthuntWrapper>
);
};

View File

@ -43,8 +43,8 @@ const NotFound: React.FC = () => {
<StyledImageWrapper> <StyledImageWrapper>
<img src="/assets/404.svg" alt="not found" width={300} height={400} /> <img src="/assets/404.svg" alt="not found" width={300} height={400} />
</StyledImageWrapper> </StyledImageWrapper>
<Title color="dark">WIZARDS BEHIND CURTAINS?</Title> <Title c="dark">WIZARDS BEHIND CURTAINS?</Title>
<Text color="dark">Looks like you&apos;re lost, let&apos;s head back to the home!</Text> <Text c="dark">Looks like you&apos;re lost, let&apos;s head back to the home!</Text>
<Button mt="lg" size="lg" type="button" onClick={() => router.push("/")}> <Button mt="lg" size="lg" type="button" onClick={() => router.push("/")}>
Go Home Go Home
</Button> </Button>

View File

@ -2,8 +2,10 @@ import React from "react";
import type { AppProps } from "next/app"; import type { AppProps } from "next/app";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { StyleSheetManager, ThemeProvider } from "styled-components"; import { MantineProvider, createTheme, ColorSchemeScript } from "@mantine/core";
import { MantineProvider, MantineThemeOverride } from "@mantine/core"; import "@mantine/core/styles.css";
import "@mantine/code-highlight/styles.css";
import { ThemeProvider } from "styled-components";
import { SessionContextProvider, Session } from "@supabase/auth-helpers-react"; import { SessionContextProvider, Session } from "@supabase/auth-helpers-react";
import ReactGA from "react-ga4"; import ReactGA from "react-ga4";
import { monaSans } from "src/constants/fonts"; import { monaSans } from "src/constants/fonts";
@ -12,6 +14,12 @@ import { lightTheme } from "src/constants/theme";
import { supabase } from "src/lib/api/supabase"; import { supabase } from "src/lib/api/supabase";
import useUser from "src/store/useUser"; import useUser from "src/store/useUser";
const mantineTheme = createTheme({
fontFamily: monaSans.style.fontFamily,
headings: { fontFamily: monaSans.style.fontFamily },
primaryShade: 8,
});
const isDevelopment = process.env.NODE_ENV === "development"; const isDevelopment = process.env.NODE_ENV === "development";
const GA_TRACKING_ID = process.env.NEXT_PUBLIC_GA_ID; const GA_TRACKING_ID = process.env.NEXT_PUBLIC_GA_ID;
@ -21,13 +29,6 @@ const Toaster = dynamic(() => import("react-hot-toast").then(c => c.Toaster));
const ExternalMode = dynamic(() => import("src/layout/ExternalMode")); const ExternalMode = dynamic(() => import("src/layout/ExternalMode"));
const ModalController = dynamic(() => import("src/layout/ModalController")); const ModalController = dynamic(() => import("src/layout/ModalController"));
const mantineTheme: MantineThemeOverride = {
colorScheme: "light",
fontFamily: monaSans.style.fontFamily,
headings: { fontFamily: monaSans.style.fontFamily },
primaryShade: 8,
};
function JsonCrack({ function JsonCrack({
Component, Component,
pageProps, pageProps,
@ -57,31 +58,29 @@ function JsonCrack({
return ( return (
<SessionContextProvider supabaseClient={supabase}> <SessionContextProvider supabaseClient={supabase}>
<StyleSheetManager> <MantineProvider theme={mantineTheme}>
<ThemeProvider theme={lightTheme}> <ThemeProvider theme={lightTheme}>
<GlobalStyle /> <GlobalStyle />
<MantineProvider theme={mantineTheme} withGlobalStyles withNormalizeCSS withCSSVariables> <Component {...pageProps} />
<Component {...pageProps} /> <ModalController />
<ModalController /> <Toaster
<Toaster position="top-right"
position="top-right" containerStyle={{
containerStyle={{ top: 40,
top: 40, right: 6,
right: 6, fontSize: 14,
fontSize: 14, }}
}} toastOptions={{
toastOptions={{ style: {
style: { background: "#4D4D4D",
background: "#4D4D4D", color: "#B9BBBE",
color: "#B9BBBE", borderRadius: 4,
borderRadius: 4, },
}, }}
}} />
/> <ExternalMode />
<ExternalMode />
</MantineProvider>
</ThemeProvider> </ThemeProvider>
</StyleSheetManager> </MantineProvider>
</SessionContextProvider> </SessionContextProvider>
); );
} }

View File

@ -7,11 +7,8 @@ import Document, {
DocumentInitialProps, DocumentInitialProps,
} from "next/document"; } from "next/document";
import { ServerStyleSheet } from "styled-components"; import { ServerStyleSheet } from "styled-components";
import { createGetInitialProps } from "@mantine/next";
import { SeoTags } from "src/components/SeoTags"; import { SeoTags } from "src/components/SeoTags";
const getInitialProps = createGetInitialProps();
class MyDocument extends Document { class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext): Promise<DocumentInitialProps> { static async getInitialProps(ctx: DocumentContext): Promise<DocumentInitialProps> {
const sheet = new ServerStyleSheet(); const sheet = new ServerStyleSheet();
@ -23,7 +20,7 @@ class MyDocument extends Document {
enhanceApp: App => props => sheet.collectStyles(<App {...props} />), enhanceApp: App => props => sheet.collectStyles(<App {...props} />),
}); });
const initialProps = await getInitialProps(ctx); const initialProps = await Document.getInitialProps(ctx);
return { return {
...initialProps, ...initialProps,

View File

@ -1,8 +1,8 @@
import React from "react"; import React from "react";
import Head from "next/head"; import Head from "next/head";
import styled from "styled-components"; import styled from "styled-components";
import { Group, MediaQuery, Paper, Stack, Text, Title } from "@mantine/core"; import { CodeHighlight } from "@mantine/code-highlight";
import { Prism } from "@mantine/prism"; import { Group, Paper, Stack, Text, Title } from "@mantine/core";
import Layout from "src/layout/Layout"; import Layout from "src/layout/Layout";
const StyledFrame = styled.iframe` const StyledFrame = styled.iframe`
@ -46,12 +46,12 @@ const Docs = () => {
</Head> </Head>
<Stack mx="auto" maw="75%"> <Stack mx="auto" maw="75%">
<Group mb="lg" mt={40}> <Group mb="lg" mt={40}>
<Title order={1} color="dark"> <Title order={1} c="dark">
Documentation Documentation
</Title> </Title>
</Group> </Group>
<Paper p="md" radius="md" withBorder> <Paper p="md" radius="md" withBorder>
<Title order={3} color="dark"> <Title order={3} c="dark">
# Fetching from URL # Fetching from URL
</Title> </Title>
<StyledContentBody> <StyledContentBody>
@ -81,7 +81,7 @@ const Docs = () => {
</StyledContentBody> </StyledContentBody>
</Paper> </Paper>
<Paper p="md" radius="md" withBorder> <Paper p="md" radius="md" withBorder>
<Title order={2} color="dark"> <Title order={2} c="dark">
# Embed Saved JSON # Embed Saved JSON
</Title> </Title>
<StyledContentBody> <StyledContentBody>
@ -102,7 +102,7 @@ const Docs = () => {
</StyledContentBody> </StyledContentBody>
</Paper> </Paper>
<Paper p="md" radius="md" withBorder> <Paper p="md" radius="md" withBorder>
<Title order={2} color="dark"> <Title order={2} c="dark">
# Communicating with API # Communicating with API
</Title> </Title>
<h3> Post Message to Embed</h3> <h3> Post Message to Embed</h3>
@ -118,13 +118,14 @@ const Docs = () => {
</StyledHighlight> </StyledHighlight>
, you should pass an object consist of &quot;json&quot; and &quot;options&quot; key , you should pass an object consist of &quot;json&quot; and &quot;options&quot; key
where json is a string and options is an object that may contain the following: where json is a string and options is an object that may contain the following:
<MediaQuery smallerThan="sm" styles={{ display: "none" }}> <CodeHighlight
<Prism w={500} language="json"> w={500}
{ language="json"
'{\n theme: "light" | "dark",\n direction: "TOP" | "RIGHT" | "DOWN" | "LEFT"\n}' code={
} '{\n theme: "light" | "dark",\n direction: "TOP" | "RIGHT" | "DOWN" | "LEFT"\n}'
</Prism> }
</MediaQuery> withCopyButton={false}
/>
</Text> </Text>
<StyledFrame <StyledFrame

View File

@ -33,7 +33,7 @@ function ResetPassword() {
return ( return (
<Paper mx="auto" mt={70} maw={400} p="lg" withBorder> <Paper mx="auto" mt={70} maw={400} p="lg" withBorder>
<Text size="lg" weight={500} mb="lg"> <Text size="lg" w={500} mb="lg">
Create New Password Create New Password
</Text> </Text>
@ -46,6 +46,7 @@ function ResetPassword() {
label="New Password" label="New Password"
radius="sm" radius="sm"
placeholder="" placeholder=""
style={{ color: "black" }}
/> />
<PasswordInput <PasswordInput
value={password2} value={password2}
@ -54,10 +55,11 @@ function ResetPassword() {
label="Validate Password" label="Validate Password"
radius="sm" radius="sm"
placeholder="" placeholder=""
style={{ color: "black" }}
/> />
</Stack> </Stack>
<Group position="apart" mt="xl"> <Group justify="apart" mt="xl">
<Button color="dark" type="submit" radius="sm" loading={loading} fullWidth> <Button color="dark" type="submit" radius="sm" loading={loading} fullWidth>
Reset Password Reset Password
</Button> </Button>
@ -101,7 +103,7 @@ const ForgotPassword = () => {
<ResetPassword /> <ResetPassword />
) : ( ) : (
<Paper mx="auto" mt={70} maw={400} p="lg" withBorder> <Paper mx="auto" mt={70} maw={400} p="lg" withBorder>
<Text size="lg" weight={500}> <Text size="lg" w={500} c="dark">
Reset Password Reset Password
</Text> </Text>
<Paper pt="lg"> <Paper pt="lg">
@ -118,16 +120,17 @@ const ForgotPassword = () => {
label="Email" label="Email"
placeholder="hello@herowand.com" placeholder="hello@herowand.com"
radius="sm" radius="sm"
style={{ color: "black" }}
/> />
</Stack> </Stack>
<Group position="apart" mt="xl"> <Group justify="apart" mt="xl">
<Button color="dark" type="submit" radius="sm" loading={loading} fullWidth> <Button color="dark" type="submit" radius="sm" loading={loading} fullWidth>
Reset Password Reset Password
</Button> </Button>
</Group> </Group>
<Stack mt="lg" align="center"> <Stack mt="lg" align="center">
<Anchor component={Link} prefetch={false} href="/sign-in" color="dark" size="xs"> <Anchor component={Link} prefetch={false} href="/sign-in" c="dark" size="xs">
Don&apos;t have an account? Sign Up Don&apos;t have an account? Sign Up
</Anchor> </Anchor>
</Stack> </Stack>

View File

@ -1,53 +1,71 @@
import React from "react"; import React from "react";
import dynamic from "next/dynamic";
import Head from "next/head"; import Head from "next/head";
import Link from "next/link"; import Link from "next/link";
import Script from "next/script"; import Script from "next/script";
import styled, { ThemeProvider } from "styled-components"; import { Button, Group, Stack, Title, Text } from "@mantine/core";
import { import styled from "styled-components";
Anchor,
Button,
Center,
Container,
Flex,
Group,
MediaQuery,
Stack,
Text,
Title,
Tooltip,
rem,
} from "@mantine/core";
import { FaChevronRight } from "react-icons/fa"; import { FaChevronRight } from "react-icons/fa";
import { SiVisualstudiocode } from "react-icons/si";
import { Typewriter } from "react-simple-typewriter"; import { Typewriter } from "react-simple-typewriter";
import { HovercardAds } from "src/components/HovercardAds"; import Layout from "src/layout/Layout";
import { lightTheme } from "src/constants/theme";
import { FeaturesCards } from "src/containers/Features";
import { Navbar } from "src/layout/Navbar";
const Footer = dynamic(() => import("src/layout/Footer").then(c => c.Footer));
const StyledHeroSection = styled.div` const StyledHeroSection = styled.div`
--bg-color: ${({ theme }) => theme.GRID_BG_COLOR}; position: relative;
--line-color-1: ${({ theme }) => theme.GRID_COLOR_PRIMARY}; background-size: 100% 100%;
--line-color-2: ${({ theme }) => theme.GRID_COLOR_SECONDARY}; margin-bottom: -48px;
background-color: var(--bg-color);
background-image: linear-gradient(var(--line-color-1) 1.5px, transparent 1.5px),
linear-gradient(90deg, var(--line-color-1) 1.5px, transparent 1.5px),
linear-gradient(var(--line-color-2) 1px, transparent 1px),
linear-gradient(90deg, var(--line-color-2) 1px, transparent 1px);
background-position: background-position:
-1.5px -1.5px, 0px 0px,
-1.5px -1.5px, 0px 0px,
-1px -1px, 0px 0px,
-1px -1px; 0px 0px,
background-size: 0px 0px;
100px 100px, background-image: radial-gradient(49% 81% at 45% 47%, #26001fff 1%, #60006a00 100%),
100px 100px, radial-gradient(113% 91% at 17% -2%, #0f000cff 1%, #ff000000 99%),
20px 20px, radial-gradient(142% 91% at 83% 7%, #0f000cff 1%, #ff000000 99%),
20px 20px; radial-gradient(142% 91% at -6% 74%, #0f000cff 1%, #ff000000 99%),
radial-gradient(142% 91% at 111% 84%, #0f000cff 0%, #5b004eff 99%);
overflow: hidden;
@keyframes shine {
0% {
background-position:
0 0,
3px 60px,
130px 270px,
70px 100px;
}
100% {
background-position:
1000px 1000px,
1000px 1000px,
1000px 1000px,
1000px 1000px;
}
}
&::before {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100vh;
content: "";
background-color: transparent;
background-image: radial-gradient(white, rgba(255, 255, 255, 0.2) 2px, transparent 2px),
radial-gradient(white, rgba(255, 255, 255, 0.15) 1px, transparent 1px),
radial-gradient(white, rgba(255, 255, 255, 0.1) 2px, transparent 2px),
radial-gradient(rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.1) 2px, transparent 1px);
background-size:
800px 800px,
400px 400px,
300px 300px,
200px 200px;
background-position:
0 0,
3px 60px,
130px 270px,
70px 100px;
animation: shine 100s linear infinite alternate; /* Use alternate to make it smoother */
}
@media only screen and (max-width: 1240px) { @media only screen and (max-width: 1240px) {
flex-direction: column; flex-direction: column;
@ -63,231 +81,94 @@ const StyledHeroSectionBody = styled.div`
overflow: hidden; overflow: hidden;
backdrop-filter: blur(1px); backdrop-filter: blur(1px);
-webkit-backdrop-filter: blur(1px); -webkit-backdrop-filter: blur(1px);
height: 70vh; height: calc(100vh - 56px);
text-align: center;
@media only screen and (max-width: 1200px) { @media only screen and (max-width: 1200px) {
flex-direction: column; flex-direction: column;
} }
`; `;
const Left = styled.div` const StyledHighlightedText = styled(Text)`
width: 100%; font-size: 40px;
z-index: 1; font-weight: 800;
`; display: inline;
const Right = styled.div` background-color: gray;
position: absolute; background-image: linear-gradient(45deg, gray, slategray);
top: 0; background-size: 100%;
right: 0; -webkit-background-clip: text;
transform: translate(20em, 15em) rotate(3deg); background-clip: text;
width: 80%; -moz-background-clip: text;
filter: blur(1px); -webkit-text-fill-color: transparent;
user-select: none; -moz-text-fill-color: transparent;
@media only screen and (max-width: 600px) {
display: none;
}
`;
const StyledHighlightedText = styled.span`
text-decoration: underline;
text-decoration-style: wavy;
text-decoration-color: #eab308;
`; `;
const StyledHeroText = styled.p` const StyledHeroText = styled.p`
font-size: 18px; font-size: 18px;
color: #5e656b; color: #a9aaaa;
font-weight: 600; font-weight: 600;
max-width: 600px; max-width: 600px;
text-align: center;
@media only screen and (max-width: 600px) { @media only screen and (max-width: 600px) {
max-width: 100%; max-width: 100%;
} }
`; `;
const StyledStatsWrapper = styled.div`
display: flex;
gap: 24px;
justify-content: center;
align-items: center;
background: #421665;
padding: 24px;
@media screen and (max-width: 768px) {
flex-direction: column;
}
`;
const HeroSection = () => ( const HeroSection = () => (
<StyledHeroSection id="hero-section"> <StyledHeroSection id="hero-section">
<Navbar />
<StyledHeroSectionBody> <StyledHeroSectionBody>
<Left> <Stack w="100%" mx="auto" align="center">
<Stack w="100%" mx="auto"> <Title c="#d0c9c9" order={1} fz={40} fw={800} style={{ textAlign: "center" }}>
<MediaQuery query="(max-width: 40em)" styles={{ fontSize: rem(30) }}> Understand your{" "}
<Title order={1} fz={40} c="gray" fw={800}> <StyledHighlightedText>
Visualize{" "} <Typewriter
<StyledHighlightedText> words={["JSON", "YAML", "XML", "TOML", "CSV"]}
<Typewriter typeSpeed={100}
words={["JSON", "YAML", "XML", "TOML", "CSV"]} deleteSpeed={60}
typeSpeed={100} delaySpeed={2000}
deleteSpeed={60} loop
delaySpeed={2000} />
loop </StyledHighlightedText>
/> <br />
</StyledHighlightedText> better by visualizing
<br /> </Title>
instantly into
<Text
variant="gradient"
gradient={{ from: "purple", to: "orange", deg: 45 }}
display="inline"
>
{" "}
graphs
</Text>
</Title>
</MediaQuery>
<StyledHeroText> <StyledHeroText>
Visualize, analyze, and manipulate data with ease, a versatile and powerful tool for Visualize, analyze, and manipulate data with ease, a versatile and powerful tool for data
data representation and exploration. representation and exploration.
</StyledHeroText> </StyledHeroText>
<Group spacing="xl"> <Group gap="xl">
<Button <Button
component={Link} color="orange"
href="/editor" variant="light"
prefetch={false} component={Link}
fw="bold" href="/editor"
rightIcon={<FaChevronRight />} fw="bold"
size="lg" rightSection={<FaChevronRight />}
> size="xl"
GO TO EDITOR style={{ border: "2px solid orange" }}
</Button> >
<Tooltip GO TO EDITOR
maw={400} </Button>
label="VS Code extension only contains JSON visualization without additional features." </Group>
withArrow </Stack>
multiline
position="bottom"
>
<Anchor
href="https://marketplace.visualstudio.com/items?itemName=AykutSarac.jsoncrack-vscode"
target="_blank"
fw="bold"
>
<Flex gap="xs" align="center">
<SiVisualstudiocode />
Get it on VS Code
</Flex>
</Anchor>
</Tooltip>
</Group>
</Stack>
</Left>
<Right>
<img
src="/assets/diagram_bg.webp"
width="1200"
height="593"
alt="diagram"
loading="eager"
fetchPriority="high"
/>
</Right>
</StyledHeroSectionBody> </StyledHeroSectionBody>
</StyledHeroSection> </StyledHeroSection>
); );
const StatsBanner = () => (
<StyledStatsWrapper>
<Flex gap="lg">
<Stack spacing="0">
<Text fw="bolder" fz="1.8rem" truncate>
24.8K
</Text>
<Text color="gray.5" fw="bold" fz="0.8rem" truncate>
GITHUB STARS
</Text>
</Stack>
<Stack spacing="0">
<Text fw="bolder" fz="1.8rem" truncate>
50K+
</Text>
<Text color="gray.5" fw="bold" fz="12px" truncate>
MONTHLY USERS
</Text>
</Stack>
<Stack spacing="0">
<Text fw="bolder" fz="1.8rem" truncate>
GPL-3
</Text>
<Text color="gray.5" fw="bold" fz="0.8rem" truncate>
LICENSE
</Text>
</Stack>
</Flex>
<Stack ml={60}>
<Text maw={600} fw="bold" fz="0.9rem">
JSON Crack is an open-source project under GPL-3 license. Support us through our premium
plan for continued development and exclusive benefits.
</Text>
<Anchor
component={Link}
href="/pricing"
prefetch={false}
color="yellow"
fw="bold"
w="fit-content"
>
View Premium Plan <FaChevronRight />
</Anchor>
</Stack>
</StyledStatsWrapper>
);
const HeroBottom = () => (
<Container mt={100}>
<Stack>
<Title color="dark" order={2} fz="xl" maw={500} mx="auto" align="center">
But that&apos;s not all yet!
<br />
Explore the full potential of your data now......
</Title>
<Center>
<Button
component={Link}
href="/editor"
prefetch={false}
color="violet"
fw="bold"
rightIcon={<FaChevronRight />}
size="lg"
>
GO TO EDITOR
</Button>
</Center>
</Stack>
</Container>
);
export const HomePage = () => { export const HomePage = () => {
const [ads, setAds] = React.useState(false); const [ads, setAds] = React.useState(false);
return ( return (
<ThemeProvider theme={lightTheme}> <Layout>
<Head> <Head>
<title>JSON Crack | Visualize Instantly Into Graphs</title> <title>JSON Crack | Visualize Instantly Into Graphs</title>
</Head> </Head>
<HeroSection /> <HeroSection />
<StatsBanner />
<FeaturesCards />
<HeroBottom />
<Footer />
{ads && <HovercardAds />}
<Script src="https://m.servedby-buysellads.com/monetization.js" onLoad={() => setAds(true)} /> <Script src="https://m.servedby-buysellads.com/monetization.js" onLoad={() => setAds(true)} />
</ThemeProvider> </Layout>
); );
}; };

View File

@ -9,39 +9,49 @@ const Privacy = () => {
<Head> <Head>
<title>Privacy Policy - JSON Crack</title> <title>Privacy Policy - JSON Crack</title>
</Head> </Head>
<Container my={50} size="sm"> <Container my={50} size="sm" pb="lg">
<Paper bg="transparent"> <Paper bg="transparent">
<Title>Privacy Policy</Title> <Title c="dark">Privacy Policy</Title>
<Stack my="lg"> <Stack my="lg">
<Text> <Text c="dark">
This Privacy Policy describes how your personal information is collected, used, and This Privacy Policy describes how your personal information is collected, used, and
shared when you visit or make a purchase from jsoncrack.com (the Site). shared when you visit or make a purchase from jsoncrack.com (the Site).
</Text> </Text>
<Title order={3}>To Whom Does This Policy Apply</Title> <Title order={3} c="dark">
<Text> To Whom Does This Policy Apply
</Title>
<Text c="dark">
This Privacy Policy applies to customers and site visitors. Each customer is This Privacy Policy applies to customers and site visitors. Each customer is
responsible for posting its own terms, conditions, and privacy policies, and ensuring responsible for posting its own terms, conditions, and privacy policies, and ensuring
compliance with all applicable laws and regulations. compliance with all applicable laws and regulations.
</Text> </Text>
<Title order={3}>Changes To This Privacy Policy</Title> <Title order={3} c="dark">
<Text> Changes To This Privacy Policy
</Title>
<Text c="dark">
This Privacy Policy may change from time to time, as our Platform and our business may This Privacy Policy may change from time to time, as our Platform and our business may
change. Your continued use of the Platform after any changes to this Privacy Policy change. Your continued use of the Platform after any changes to this Privacy Policy
indicates your agreement with the terms of the revised Privacy Policy. indicates your agreement with the terms of the revised Privacy Policy.
</Text> </Text>
<Title order={3}>What Information Do We Collect</Title> <Title order={3} c="dark">
<Text> What Information Do We Collect
</Title>
<Text c="dark">
We collect information directly from you when you provide it to us explicitly on our We collect information directly from you when you provide it to us explicitly on our
Site. We do not use third-party cookies on our Site. Site. We do not use third-party cookies on our Site.
</Text> </Text>
<Title order={3}>What We Use Your Information For</Title> <Title order={3} c="dark">
<Text> What We Use Your Information For
</Title>
<Text c="dark">
We use your information to provide our Services, to improve our Platform, to We use your information to provide our Services, to improve our Platform, to
understand how you use our Platform, and to communicate with you. We DO NOT share any understand how you use our Platform, and to communicate with you. We DO NOT share any
personal information to third parties. personal information to third parties.
</Text> </Text>
<Title order={3}>How To Contact Us</Title> <Title order={3} c="dark">
<Text> How To Contact Us
</Title>
<Text c="dark">
For privacy-related questions, please contact us at{" "} For privacy-related questions, please contact us at{" "}
<Anchor href="mailto:contact@jsoncrack.com">contact@jsoncrack.com</Anchor>. <Anchor href="mailto:contact@jsoncrack.com">contact@jsoncrack.com</Anchor>.
</Text> </Text>

View File

@ -9,25 +9,29 @@ const SubscriptionRefund = () => {
<Head> <Head>
<title>Subscription & Refund - JSON Crack</title> <title>Subscription & Refund - JSON Crack</title>
</Head> </Head>
<Container my={50} size="sm"> <Container my={50} size="sm" pb="lg">
<Paper bg="transparent"> <Paper bg="transparent">
<Title>Subscription & Refund</Title> <Title c="dark">Subscription & Refund</Title>
<Stack my="lg"> <Stack my="lg">
<Text> <Text c="dark">
This document delineates the Subscription Cancellation and Refund Policy for users of This document delineates the Subscription Cancellation and Refund Policy for users of
jsoncrack.com (the Site). It provides guidance on the cancellation process. For jsoncrack.com (the Site). It provides guidance on the cancellation process. For
inquiries or assistance related to cancellations, users are encouraged to contact the inquiries or assistance related to cancellations, users are encouraged to contact the
customer support team at{" "} customer support team at{" "}
<Anchor href="mailto:contact@jsoncrack.com">contact@jsoncrack.com</Anchor>. <Anchor href="mailto:contact@jsoncrack.com">contact@jsoncrack.com</Anchor>.
</Text> </Text>
<Title order={3}>Cancellation Policy</Title> <Title order={3} c="dark">
<Text> Cancellation Policy
</Title>
<Text c="dark">
You have the right to cancel your subscription at any time. When you cancel, your You have the right to cancel your subscription at any time. When you cancel, your
subscription will remain active until the end of the current billing period. You will subscription will remain active until the end of the current billing period. You will
not be billed for any subsequent periods. not be billed for any subsequent periods.
</Text> </Text>
<Title order={4}>How to Cancel:</Title> <Title order={4} c="dark">
<Text> How to Cancel:
</Title>
<Text c="dark">
To cancel your subscription, follow these steps: To cancel your subscription, follow these steps:
<List type="ordered" my="lg"> <List type="ordered" my="lg">
<List.Item>Log in to your account.</List.Item> <List.Item>Log in to your account.</List.Item>
@ -40,16 +44,20 @@ const SubscriptionRefund = () => {
not eligible for a refund. However, you will still have access to the service until not eligible for a refund. However, you will still have access to the service until
the end of the current billing period. the end of the current billing period.
</Text> </Text>
<Title order={3}>Refund Policy</Title> <Title order={3} c="dark">
<Text> Refund Policy
</Title>
<Text c="dark">
If you cancel your subscription within 3 days of the initial purchase, you are If you cancel your subscription within 3 days of the initial purchase, you are
eligible for a full refund. Refunds will be issued to the original payment method used eligible for a full refund. Refunds will be issued to the original payment method used
during the purchase. For refund inquiries or assistance, please contact our customer during the purchase. For refund inquiries or assistance, please contact our customer
support team at{" "} support team at{" "}
<Anchor href="mailto:contact@jsoncrack.com">contact@jsoncrack.com</Anchor>. <Anchor href="mailto:contact@jsoncrack.com">contact@jsoncrack.com</Anchor>.
</Text> </Text>
<Title order={3}>Changes to this Policy</Title> <Title order={3} c="dark">
<Text> Changes to this Policy
</Title>
<Text c="dark">
We reserve the right to modify this subscription cancellation and refund policy at any We reserve the right to modify this subscription cancellation and refund policy at any
time. Any changes will be effective immediately upon posting the updated policy on our time. Any changes will be effective immediately upon posting the updated policy on our
website. It is your responsibility to review this policy periodically for changes. By website. It is your responsibility to review this policy periodically for changes. By

View File

@ -9,11 +9,11 @@ const Terms = () => {
<Head> <Head>
<title>Terms of Service - JSON Crack</title> <title>Terms of Service - JSON Crack</title>
</Head> </Head>
<Container my={50} size="sm"> <Container my={50} size="sm" pb="lg">
<Paper bg="transparent"> <Paper bg="transparent">
<Title>Terms of Service</Title> <Title c="dark">Terms of Service</Title>
<Stack my="lg"> <Stack my="lg">
<Text> <Text c="dark">
Subject to these Terms of Service (this &apos;Agreement&apos;), jsoncrack.com Subject to these Terms of Service (this &apos;Agreement&apos;), jsoncrack.com
(&apos;JSON Crack&apos;, &apos;we&apos;, &apos;us&apos; and/or &apos;our&apos;) (&apos;JSON Crack&apos;, &apos;we&apos;, &apos;us&apos; and/or &apos;our&apos;)
provides access to JSON Cracks application as a service (collectively, the provides access to JSON Cracks application as a service (collectively, the
@ -21,7 +21,7 @@ const Terms = () => {
have read, understand, and agree to be bound by this Agreement. have read, understand, and agree to be bound by this Agreement.
</Text> </Text>
<Text> <Text c="dark">
If you are entering into this Agreement on behalf of a company, business or other If you are entering into this Agreement on behalf of a company, business or other
legal entity, you represent that you have the authority to bind such an entity to this legal entity, you represent that you have the authority to bind such an entity to this
Agreement, in which case the term &apos;you&apos; shall refer to such entity. If you Agreement, in which case the term &apos;you&apos; shall refer to such entity. If you
@ -29,30 +29,38 @@ const Terms = () => {
accept this Agreement and may not use the Services. accept this Agreement and may not use the Services.
</Text> </Text>
<Title order={3}>1. Acceptance of Terms</Title> <Title order={3} c="dark">
<Text> 1. Acceptance of Terms
</Title>
<Text c="dark">
By signing up and using the services provided by JSON Crack (referred to as the By signing up and using the services provided by JSON Crack (referred to as the
&apos;Service&apos;), you are agreeing to be bound by the following terms and &apos;Service&apos;), you are agreeing to be bound by the following terms and
conditions (&apos;Terms of Service&apos;). The Service is owned and operated by JSON conditions (&apos;Terms of Service&apos;). The Service is owned and operated by JSON
Crack (&apos;Us&apos;, &apos;We&apos;, or &apos;Our&apos;). Crack (&apos;Us&apos;, &apos;We&apos;, or &apos;Our&apos;).
</Text> </Text>
<Title order={3}>2. Description of Service</Title> <Title order={3} c="dark">
<Text> 2. Description of Service
</Title>
<Text c="dark">
JSON Crack is an open-source visualization application that allows users to transform JSON Crack is an open-source visualization application that allows users to transform
various data formats, including JSON, YAML, XML, CSV, and more, into interactive various data formats, including JSON, YAML, XML, CSV, and more, into interactive
graphs for visualization purposes (the Product). The Product is accessible at graphs for visualization purposes (the Product). The Product is accessible at
jsoncrack.com and other domains and subdomains controlled by Us (collectively, jsoncrack.com and other domains and subdomains controlled by Us (collectively,
&apos;the Website&apos;). &apos;the Website&apos;).
</Text> </Text>
<Title order={3}>3. Fair Use</Title> <Title order={3} c="dark">
<Text> 3. Fair Use
</Title>
<Text c="dark">
We reserve the right to suspend or terminate your access to the Service if we We reserve the right to suspend or terminate your access to the Service if we
determine, in our sole discretion, that you have violated these Terms of Service, determine, in our sole discretion, that you have violated these Terms of Service,
including but not limited to, purposefully advertising of third parties, spam content, including but not limited to, purposefully advertising of third parties, spam content,
or other inappropriate or illegal content. or other inappropriate or illegal content.
</Text> </Text>
<Title order={3}>4. Intellectual Property Rights</Title> <Title order={3} c="dark">
<Text> 4. Intellectual Property Rights
</Title>
<Text c="dark">
You acknowledge and agree that the Service and its entire contents, features, and You acknowledge and agree that the Service and its entire contents, features, and
functionality, including but not limited to all information, software, code, text, functionality, including but not limited to all information, software, code, text,
displays, graphics, photographs, video, audio, design, presentation, selection, and displays, graphics, photographs, video, audio, design, presentation, selection, and
@ -60,36 +68,44 @@ const Terms = () => {
are protected by United States and international copyright, trademark, patent, trade are protected by United States and international copyright, trademark, patent, trade
secret, and other intellectual property or proprietary rights laws. secret, and other intellectual property or proprietary rights laws.
</Text> </Text>
<Title order={3}>5. Data Storage and Privacy</Title> <Title order={3} c="dark">
<Text> 5. Data Storage and Privacy
</Title>
<Text c="dark">
We do not guarantee the security, reliability, consistency and/or recovery of any data We do not guarantee the security, reliability, consistency and/or recovery of any data
stored in the Product and do not recommend storing sensitive data. The Product enables stored in the Product and do not recommend storing sensitive data. The Product enables
users to store data for visualization purposes as an ease of access only. When a file users to store data for visualization purposes as an ease of access only. When a file
is deleted from the Product, it is also permanently removed from the database (refer is deleted from the Product, it is also permanently removed from the database (refer
as hard deletion). as hard deletion).
</Text> </Text>
<Title order={3}>6. Changes to these Terms</Title> <Title order={3} c="dark">
<Text> 6. Changes to these Terms
</Title>
<Text c="dark">
We reserve the right to revise and update these Terms of Service from time to time in We reserve the right to revise and update these Terms of Service from time to time in
our sole discretion. All changes are effective immediately when we post them, and our sole discretion. All changes are effective immediately when we post them, and
apply to all access to and use of the Website thereafter. Your continued use of the apply to all access to and use of the Website thereafter. Your continued use of the
Website following the posting of revised Terms of Service means that you accept and Website following the posting of revised Terms of Service means that you accept and
agree to the changes. agree to the changes.
</Text> </Text>
<Title order={3}>7. Contact Information</Title> <Title order={3} c="dark">
<Text> 7. Contact Information
</Title>
<Text c="dark">
Questions or comments about the Website or these Terms of Service may be directed to Questions or comments about the Website or these Terms of Service may be directed to
our support team at{" "} our support team at{" "}
<Anchor href="mailto:contact@jsoncrack.com">contact@jsoncrack.com</Anchor>. <Anchor href="mailto:contact@jsoncrack.com">contact@jsoncrack.com</Anchor>.
</Text> </Text>
<Title order={3}>8. Disclaimer of Warranties</Title> <Title order={3} c="dark">
<Text> 8. Disclaimer of Warranties
</Title>
<Text c="dark">
THE SERVICE AND ITS CONTENT ARE PROVIDED ON AN &apos;AS IS&apos; AND &apos;AS THE SERVICE AND ITS CONTENT ARE PROVIDED ON AN &apos;AS IS&apos; AND &apos;AS
AVAILABLE&apos; BASIS WITHOUT ANY WARRANTIES OF ANY KIND. WE DISCLAIM ALL WARRANTIES, AVAILABLE&apos; BASIS WITHOUT ANY WARRANTIES OF ANY KIND. WE DISCLAIM ALL WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE WARRANTY OF TITLE, MERCHANTABILITY, INCLUDING, BUT NOT LIMITED TO, THE WARRANTY OF TITLE, MERCHANTABILITY,
NON-INFRINGEMENT OF THIRD PARTIES RIGHTS, AND FITNESS FOR PARTICULAR PURPOSE. NON-INFRINGEMENT OF THIRD PARTIES RIGHTS, AND FITNESS FOR PARTICULAR PURPOSE.
</Text> </Text>
<Text> <Text c="dark">
IN NO EVENT WILL WE, OUR AFFILIATES OR THEIR LICENSORS, SERVICE PROVIDERS, EMPLOYEES, IN NO EVENT WILL WE, OUR AFFILIATES OR THEIR LICENSORS, SERVICE PROVIDERS, EMPLOYEES,
AGENTS, OFFICERS OR DIRECTORS BE LIABLE FOR DAMAGES OF ANY KIND, UNDER ANY LEGAL AGENTS, OFFICERS OR DIRECTORS BE LIABLE FOR DAMAGES OF ANY KIND, UNDER ANY LEGAL
THEORY, ARISING OUT OF OR IN CONNECTION WITH YOUR USE, OR INABILITY TO USE, THE THEORY, ARISING OUT OF OR IN CONNECTION WITH YOUR USE, OR INABILITY TO USE, THE

View File

@ -1,70 +0,0 @@
import React from "react";
import Head from "next/head";
import { Button, Center, Container, Grid, Image, Text, Title } from "@mantine/core";
import { VscHeart } from "react-icons/vsc";
import Layout from "src/layout/Layout";
const Oss: React.FC<{ sponsors: any[] }> = ({ sponsors }) => {
return (
<Layout>
<Head>
<title>Open Source Supporters - JSON Crack</title>
</Head>
<Container pt={60}>
<Image mx="auto" src="assets/oss_banner.webp" radius="md" maw={800} alt="oss banner" />
</Container>
<Center pt="lg">
<Button
component="a"
href="https://github.com/sponsors/AykutSarac"
size="lg"
color="red"
leftIcon={<VscHeart />}
target="_blank"
fw="bolder"
>
BECOME PART OF IT
</Button>
</Center>
<Container py={50}>
<Title color="dark.4" pb="md">
Thank you!
</Title>
<Text color="dark.5" maw={500}>
&ldquo;We would like to extend our sincerest gratitude to all of our sponsors for their
invaluable support and contribution towards JSON Crack.&rdquo;
</Text>
</Container>
<Container>
<Title color="dark.3" order={3} pb="xl">
Sponsors
</Title>
<Grid gutter={30}>
{sponsors?.map(sponsor => (
<Grid.Col span="content" key={sponsor.handle}>
<a href={sponsor.profile}>
<Image radius="md" width={"4rem"} src={sponsor.avatar} alt={sponsor.handle} />
<Text color="dark.3" pt="sm" align="center" fz="xs">
{sponsor.handle}
</Text>
</a>
</Grid.Col>
))}
</Grid>
</Container>
</Layout>
);
};
export default Oss;
export async function getStaticProps() {
const res = await fetch("https://ghs.vercel.app/sponsors/aykutsarac");
const data = await res.json();
return {
props: {
sponsors: data?.sponsors.reverse() || [],
},
};
}

View File

@ -13,13 +13,13 @@ const Pricing = () => {
<title>Pricing - JSON Crack</title> <title>Pricing - JSON Crack</title>
</Head> </Head>
<Flex gap="lg" wrap="wrap" justify="center" my={60} w="fit-content" p="lg" mx="auto"> <Flex gap="lg" wrap="wrap" justify="center" my={60} w="fit-content" p="lg" mx="auto">
<Paper p="xl" radius="lg" shadow="lg" withBorder w={325}> <Paper p="xl" radius="lg" withBorder w={325}>
<Flex justify="space-between"> <Flex justify="space-between">
<Stack spacing="0"> <Stack gap="0">
<Text fz="xl" fw="bold"> <Text fz="xl" fw="bold" c="dark">
Free Free
</Text> </Text>
<Text fz="xs" fw="bold" color="gray.6"> <Text fz="xs" fw="bold" c="gray.6">
Free - forever. Free - forever.
</Text> </Text>
</Stack> </Stack>
@ -37,17 +37,20 @@ const Pricing = () => {
</ThemeIcon> </ThemeIcon>
} }
> >
<List.Item>Limited capability</List.Item> <List.Item>
<List.Item>Save & share up to 15 files</List.Item> <Text c="dark" fz="sm">
<List.Item>Visualize all data formats</List.Item> Maximum capability
<List.Item </Text>
icon={ </List.Item>
<ThemeIcon color="gray.5" size={20} radius="xl"> <List.Item>
<BsX size="1rem" /> <Text c="dark" fz="sm">
</ThemeIcon> Save & share up to 15 files
} </Text>
> </List.Item>
<Text color="gray.6">JSON Schema</Text> <List.Item>
<Text c="dark" fz="sm">
Visualize all data formats
</Text>
</List.Item> </List.Item>
<List.Item <List.Item
icon={ icon={
@ -56,7 +59,20 @@ const Pricing = () => {
</ThemeIcon> </ThemeIcon>
} }
> >
<Text color="gray.6">Edit data through graph</Text> <Text c="gray.6" fz="sm">
JSON Schema
</Text>
</List.Item>
<List.Item
icon={
<ThemeIcon color="gray.5" size={20} radius="xl">
<BsX size="1rem" />
</ThemeIcon>
}
>
<Text c="gray.6" fz="sm">
Edit data through graph
</Text>
</List.Item> </List.Item>
</List> </List>
<Button size="md" radius="md" color="orange" variant="outline"> <Button size="md" radius="md" color="orange" variant="outline">
@ -65,9 +81,9 @@ const Pricing = () => {
</Flex> </Flex>
</Paper> </Paper>
<Paper p="xl" radius="lg" shadow="lg" bg="#301e55" withBorder w={325}> <Paper p="xl" radius="lg" bg="#301e55" withBorder w={325}>
<Flex justify="space-between"> <Flex justify="space-between">
<Stack spacing="0"> <Stack gap="0">
<Text c="white" fz="xl" fw="bold"> <Text c="white" fz="xl" fw="bold">
Premium Premium
</Text> </Text>
@ -76,11 +92,11 @@ const Pricing = () => {
</Badge> </Badge>
</Stack> </Stack>
<Paper py={5} px="sm" bg="#442f71"> <Paper py={5} px="sm" bg="#442f71">
<Stack spacing="0" align="center" justify="center"> <Stack gap="0" align="center" justify="center">
<Text fz="lg" c="white" fw="bolder"> <Text fz="lg" c="white" fw="bolder">
$7 $7
</Text> </Text>
<Text fz="xs" color="gray.4" fw="bold"> <Text fz="xs" c="gray.4" fw="bold">
Per month Per month
</Text> </Text>
</Stack> </Stack>
@ -100,19 +116,29 @@ const Pricing = () => {
} }
> >
<List.Item> <List.Item>
<Text c="white">Maximum capability</Text> <Text c="white" fz="sm">
Maximum capability
</Text>
</List.Item> </List.Item>
<List.Item> <List.Item>
<Text c="white">Save & share up to 200 files</Text> <Text c="white" fz="sm">
Save & share up to 200 files
</Text>
</List.Item> </List.Item>
<List.Item> <List.Item>
<Text c="white">Visualize all data formats</Text> <Text c="white" fz="sm">
Visualize all data formats
</Text>
</List.Item> </List.Item>
<List.Item> <List.Item>
<Text c="white">JSON Schema</Text> <Text c="white" fz="sm">
JSON Schema
</Text>
</List.Item> </List.Item>
<List.Item> <List.Item>
<Text c="white">Edit data through graph</Text> <Text c="white" fz="sm">
Edit data through graph
</Text>
</List.Item> </List.Item>
</List> </List>
<Button <Button
@ -128,22 +154,22 @@ const Pricing = () => {
</Flex> </Flex>
</Paper> </Paper>
<Paper p="xl" radius="lg" shadow="lg" withBorder w={325}> <Paper p="xl" radius="lg" withBorder w={325}>
<Flex justify="space-between"> <Flex justify="space-between">
<Stack spacing="0"> <Stack gap="0">
<Text fz="xl" fw="bold"> <Text fz="xl" fw="bold" c="dark">
Enterprise Enterprise
</Text> </Text>
<Text fz="xs" fw="bold" color="gray.6"> <Text fz="xs" fw="bold" c="gray.6">
For Teams & Organizations For Teams & Organizations
</Text> </Text>
</Stack> </Stack>
<Paper py={5} px="sm" bg="gray.0"> <Paper py={5} px="sm" bg="gray.0" radius="xs">
<Stack spacing="0" align="center" justify="center"> <Stack gap="0" align="center" justify="center">
<Text fz="lg" fw="bolder"> <Text fz="lg" fw="bolder" c="dark">
$120 $120
</Text> </Text>
<Text fz="xs" color="gray.6" fw="bold"> <Text fz="xs" c="gray.6" fw="bold">
Per month Per month
</Text> </Text>
</Stack> </Stack>
@ -161,6 +187,7 @@ const Pricing = () => {
<BsCheck size="1rem" /> <BsCheck size="1rem" />
</ThemeIcon> </ThemeIcon>
} }
c="dark"
> >
<List.Item>Everything from previous plans</List.Item> <List.Item>Everything from previous plans</List.Item>
<List.Item <List.Item
@ -169,6 +196,7 @@ const Pricing = () => {
<BsCheck size="1rem" /> <BsCheck size="1rem" />
</ThemeIcon> </ThemeIcon>
} }
c="dark"
> >
Unlimited premium accounts for work email Unlimited premium accounts for work email
</List.Item> </List.Item>
@ -178,6 +206,7 @@ const Pricing = () => {
<BsCheck size="1rem" /> <BsCheck size="1rem" />
</ThemeIcon> </ThemeIcon>
} }
c="dark"
> >
Shared cloud in app Shared cloud in app
</List.Item> </List.Item>
@ -195,7 +224,7 @@ const Pricing = () => {
</Flex> </Flex>
</Paper> </Paper>
</Flex> </Flex>
<Text align="center" size="sm" color="dimmed"> <Text size="sm" c="dimmed" style={{ textAlign: "center" }}>
<AiOutlineInfoCircle style={{ marginRight: "4px" }} /> <AiOutlineInfoCircle style={{ marginRight: "4px" }} />
Payment email must be matching with the account registered to the JSON Crack. Payment email must be matching with the account registered to the JSON Crack.
</Text> </Text>

View File

@ -65,6 +65,7 @@ export function AuthenticationForm(props: PaperProps) {
value={userData.email} value={userData.email}
onChange={event => setUserData(d => ({ ...d, email: event.target.value }))} onChange={event => setUserData(d => ({ ...d, email: event.target.value }))}
radius="sm" radius="sm"
style={{ color: "black" }}
/> />
<PasswordInput <PasswordInput
@ -75,20 +76,15 @@ export function AuthenticationForm(props: PaperProps) {
value={userData.password} value={userData.password}
onChange={event => setUserData(d => ({ ...d, password: event.target.value }))} onChange={event => setUserData(d => ({ ...d, password: event.target.value }))}
radius="sm" radius="sm"
style={{ color: "black" }}
/> />
<Button color="dark" type="submit" radius="sm" loading={loading}> <Button color="dark" type="submit" radius="sm" loading={loading}>
Sign in Sign in
</Button> </Button>
<Stack spacing="sm" mx="auto" align="center"> <Stack gap="sm" mx="auto" align="center">
<Anchor <Anchor component={Link} prefetch={false} href="/forgot-password" c="dark" size="xs">
component={Link}
prefetch={false}
href="/forgot-password"
color="dark"
size="xs"
>
Forgot your password? Forgot your password?
</Anchor> </Anchor>
</Stack> </Stack>
@ -100,7 +96,7 @@ export function AuthenticationForm(props: PaperProps) {
<Stack mb="md" mt="md"> <Stack mb="md" mt="md">
<Button <Button
radius="sm" radius="sm"
leftIcon={<AiOutlineGoogle size="20" />} leftSection={<AiOutlineGoogle size="20" />}
onClick={() => handleLoginClick("google")} onClick={() => handleLoginClick("google")}
color="red" color="red"
variant="outline" variant="outline"
@ -109,7 +105,7 @@ export function AuthenticationForm(props: PaperProps) {
</Button> </Button>
<Button <Button
radius="sm" radius="sm"
leftIcon={<AiOutlineGithub size="20" />} leftSection={<AiOutlineGithub size="20" />}
onClick={() => handleLoginClick("github")} onClick={() => handleLoginClick("github")}
color="dark" color="dark"
variant="outline" variant="outline"
@ -136,11 +132,11 @@ const SignIn = () => {
<Head> <Head>
<title>Sign In - JSON Crack</title> <title>Sign In - JSON Crack</title>
</Head> </Head>
<Paper mt={50} shadow="xs" mx="auto" maw={400} p="lg" withBorder> <Paper mt={50} mx="auto" maw={400} p="lg" withBorder>
<AuthenticationForm /> <AuthenticationForm />
</Paper> </Paper>
<Center my="xl"> <Center my="xl">
<Anchor component={Link} prefetch={false} href="/sign-up" color="dark" fw="bold"> <Anchor component={Link} prefetch={false} href="/sign-up" c="dark" fw="bold">
Don&apos;t have an account? Don&apos;t have an account?
</Anchor> </Anchor>
</Center> </Center>

View File

@ -60,13 +60,13 @@ const SignUp = () => {
<title>JSON Crack | Sign Up</title> <title>JSON Crack | Sign Up</title>
</Head> </Head>
{done ? ( {done ? (
<Paper shadow="xs" mx="auto" maw={400} mt={50} p="lg" withBorder> <Paper mx="auto" maw={400} mt={50} p="lg" withBorder>
<Text align="center" mt="lg"> <Text mt="lg" style={{ textAlign: "center" }}>
Registration successul! Registration successul!
<br /> <br />
Please check your inbox for email confirmation. Please check your inbox for email confirmation.
</Text> </Text>
<Anchor component={Link} href="/sign-in" underline={false}> <Anchor component={Link} href="/sign-in">
<Button color="dark" radius="sm" mt="lg" fullWidth> <Button color="dark" radius="sm" mt="lg" fullWidth>
Back to login Back to login
</Button> </Button>
@ -74,7 +74,7 @@ const SignUp = () => {
</Paper> </Paper>
) : ( ) : (
<> <>
<Paper shadow="xs" mx="auto" maw={400} mt={50} p="lg" withBorder> <Paper mx="auto" maw={400} mt={50} p="lg" withBorder>
<form onSubmit={onSubmit}> <form onSubmit={onSubmit}>
<Stack> <Stack>
<TextInput <TextInput
@ -83,6 +83,7 @@ const SignUp = () => {
label="Name" label="Name"
placeholder="John Doe" placeholder="John Doe"
radius="sm" radius="sm"
style={{ color: "black" }}
/> />
<TextInput <TextInput
@ -92,6 +93,7 @@ const SignUp = () => {
label="Email" label="Email"
placeholder="hello@jsoncrack.com" placeholder="hello@jsoncrack.com"
radius="sm" radius="sm"
style={{ color: "black" }}
/> />
<PasswordInput <PasswordInput
@ -101,6 +103,7 @@ const SignUp = () => {
label="Password" label="Password"
placeholder="" placeholder=""
radius="sm" radius="sm"
style={{ color: "black" }}
/> />
<Button color="dark" type="submit" loading={loading}> <Button color="dark" type="submit" loading={loading}>
@ -113,7 +116,7 @@ const SignUp = () => {
<Button <Button
radius="sm" radius="sm"
fullWidth fullWidth
leftIcon={<AiOutlineGoogle size="20" />} leftSection={<AiOutlineGoogle size="20" />}
onClick={() => handleLoginClick("google")} onClick={() => handleLoginClick("google")}
color="red" color="red"
variant="outline" variant="outline"
@ -122,7 +125,7 @@ const SignUp = () => {
</Button> </Button>
<Button <Button
radius="sm" radius="sm"
leftIcon={<AiOutlineGithub size="20" />} leftSection={<AiOutlineGithub size="20" />}
onClick={() => handleLoginClick("github")} onClick={() => handleLoginClick("github")}
color="dark" color="dark"
variant="outline" variant="outline"
@ -137,15 +140,21 @@ const SignUp = () => {
<Text fz="xs" c="gray"> <Text fz="xs" c="gray">
By signing up, you agree to our{" "} By signing up, you agree to our{" "}
<Anchor component={Link} href="/legal/terms" c="gray" fw={500}> <Anchor fz="xs" component={Link} href="/legal/terms" c="gray" fw={500}>
Terms of Service Terms of Service
</Anchor>{" "} </Anchor>{" "}
and{" "} and{" "}
<Anchor component={Link} href="/legal/privacy" c="gray" fw={500}> <Anchor fz="xs" component={Link} href="/legal/privacy" c="gray" fw={500}>
Privacy Policy Privacy Policy
</Anchor> </Anchor>
. Need help?{" "} . Need help?{" "}
<Anchor component={Link} href="mailto:contact@jsoncrack.com" c="gray" fw={500}> <Anchor
fz="xs"
component={Link}
href="mailto:contact@jsoncrack.com"
c="gray"
fw={500}
>
Get in touch. Get in touch.
</Anchor> </Anchor>
</Text> </Text>
@ -154,7 +163,7 @@ const SignUp = () => {
</Paper> </Paper>
<Center my="xl"> <Center my="xl">
<Anchor component={Link} prefetch={false} href="/sign-in" color="dark" fw="bold"> <Anchor component={Link} prefetch={false} href="/sign-in" c="dark" fw="bold">
Already have an account? Already have an account?
</Anchor> </Anchor>
</Center> </Center>

View File

@ -2,11 +2,11 @@ import React from "react";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import Head from "next/head"; import Head from "next/head";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { MantineProvider, useMantineColorScheme } from "@mantine/core";
import { ThemeProvider } from "styled-components"; import { ThemeProvider } from "styled-components";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { darkTheme, lightTheme } from "src/constants/theme"; import { darkTheme, lightTheme } from "src/constants/theme";
import { Toolbar } from "src/containers/Toolbar"; import { Toolbar } from "src/containers/Toolbar";
import { EditorWrapper } from "src/layout/EditorWrapper";
import useFile from "src/store/useFile"; import useFile from "src/store/useFile";
import useGraph from "src/store/useGraph"; import useGraph from "src/store/useGraph";
@ -23,11 +23,12 @@ const Graph = dynamic(() => import("src/containers/Views/GraphView").then(c => c
const WidgetPage = () => { const WidgetPage = () => {
const { query, push, isReady } = useRouter(); const { query, push, isReady } = useRouter();
const [theme, setTheme] = React.useState("dark"); const [theme, setTheme] = React.useState<"dark" | "light">("dark");
const checkEditorSession = useFile(state => state.checkEditorSession); const checkEditorSession = useFile(state => state.checkEditorSession);
const setContents = useFile(state => state.setContents); const setContents = useFile(state => state.setContents);
const setDirection = useGraph(state => state.setDirection); const setDirection = useGraph(state => state.setDirection);
const clearGraph = useGraph(state => state.clearGraph); const clearGraph = useGraph(state => state.clearGraph);
const { setColorScheme } = useMantineColorScheme();
React.useEffect(() => { React.useEffect(() => {
if (isReady) { if (isReady) {
@ -43,6 +44,7 @@ const WidgetPage = () => {
if (!event.data?.json) return; if (!event.data?.json) return;
if (event.data?.options?.theme === "light" || event.data?.options?.theme === "dark") { if (event.data?.options?.theme === "light" || event.data?.options?.theme === "dark") {
setTheme(event.data.options.theme); setTheme(event.data.options.theme);
setColorScheme(event.data.options.theme);
} }
setContents({ contents: event.data.json, hasChanges: false }); setContents({ contents: event.data.json, hasChanges: false });
@ -58,15 +60,15 @@ const WidgetPage = () => {
}, [setContents, setDirection, theme]); }, [setContents, setDirection, theme]);
return ( return (
<EditorWrapper> <MantineProvider forceColorScheme={theme}>
<Head>
<meta name="robots" content="noindex,nofollow" />
</Head>
<ThemeProvider theme={theme === "dark" ? darkTheme : lightTheme}> <ThemeProvider theme={theme === "dark" ? darkTheme : lightTheme}>
<Head>
<meta name="robots" content="noindex,nofollow" />
</Head>
<Toolbar isWidget /> <Toolbar isWidget />
<Graph isWidget /> <Graph isWidget />
</ThemeProvider> </ThemeProvider>
</EditorWrapper> </MantineProvider>
); );
}; };

5162
yarn.lock

File diff suppressed because it is too large Load Diff