mirror of
https://github.com/AykutSarac/jsoncrack.com.git
synced 2025-01-12 19:02:53 +08:00
feat: upgrade mantine v7 & ui changes
This commit is contained in:
parent
ecb825e5e0
commit
9133475ce1
12
.github/workflows/deploy.yml
vendored
12
.github/workflows/deploy.yml
vendored
@ -26,9 +26,9 @@ jobs:
|
||||
- name: Detect package manager
|
||||
id: detect-package-manager
|
||||
run: |
|
||||
echo "manager=yarn" >> $GITHUB_OUTPUT
|
||||
echo "manager=pnpm" >> $GITHUB_OUTPUT
|
||||
echo "command=install --frozen-lockfile" >> $GITHUB_OUTPUT
|
||||
echo "runner=yarn" >> $GITHUB_OUTPUT
|
||||
echo "runner=pnpm" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
@ -40,8 +40,8 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
~/.npm
|
||||
~/.cache/yarn
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}
|
||||
~/.cache/pnpm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json', '**/pnpm-lock.yaml') }}
|
||||
restore-keys: ${{ runner.os }}-node-
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v3
|
||||
@ -52,9 +52,9 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
.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: |
|
||||
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-
|
||||
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/pnpm-lock.yaml') }}-
|
||||
- name: Install dependencies
|
||||
run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}
|
||||
- name: Build with Next.js
|
||||
|
8
.github/workflows/pull-request.yml
vendored
8
.github/workflows/pull-request.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
- run: yarn lint
|
||||
- run: yarn build
|
||||
cache: 'pnpm'
|
||||
- run: pnpm install
|
||||
- run: pnpm lint
|
||||
- run: pnpm build
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -21,8 +21,6 @@
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
|
@ -7,6 +7,8 @@
|
||||
"importOrder": [
|
||||
"^(react/(.*)$)|^(react$)",
|
||||
"^(next/(.*)$)|^(next$)",
|
||||
"^@mantine/core",
|
||||
"^@mantine",
|
||||
"styled",
|
||||
"<THIRD_PARTY_MODULES>",
|
||||
"^src/(.*)$",
|
||||
|
@ -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:
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
pnpm install
|
||||
```
|
||||
|
||||
To run the development server, you can run the following command:
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
## Contributing Guidelines
|
||||
|
@ -3,12 +3,12 @@ FROM node:18-alpine as builder
|
||||
WORKDIR /src
|
||||
|
||||
# Cache dependencies first
|
||||
COPY package.json yarn.lock ./
|
||||
RUN yarn install --frozen-lockfile
|
||||
COPY package.json pnpm-lock.yaml ./
|
||||
RUN pnpm install
|
||||
|
||||
# Copy other files and build
|
||||
COPY . /src/
|
||||
RUN yarn build
|
||||
RUN pnpm build
|
||||
|
||||
# App
|
||||
FROM nginxinc/nginx-unprivileged
|
||||
|
@ -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:
|
||||
```console
|
||||
# Install the packages
|
||||
yarn install
|
||||
pnpm install
|
||||
|
||||
# Start development server
|
||||
# Then the development server will run at http://localhost:3000
|
||||
yarn dev
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
23
package.json
23
package.json
@ -14,17 +14,14 @@
|
||||
"analyze": "ANALYZE=true npm run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/server": "^11.11.0",
|
||||
"@mantine/core": "^6.0.17",
|
||||
"@mantine/hooks": "^6.0.17",
|
||||
"@mantine/next": "^6.0.21",
|
||||
"@mantine/prism": "^6.0.21",
|
||||
"@mantine/code-highlight": "^7.3.2",
|
||||
"@mantine/core": "^7.3.2",
|
||||
"@mantine/hooks": "^7.3.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-react": "^0.4.2",
|
||||
"@supabase/supabase-js": "^2.36.0",
|
||||
"@supabase/supabase-js": "^2.39.1",
|
||||
"@tanstack/react-query": "^4.35.3",
|
||||
"allotment": "^1.19.3",
|
||||
"axios": "^1.5.0",
|
||||
@ -42,7 +39,7 @@
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.set": "^4.3.2",
|
||||
"maketypes": "^1.1.2",
|
||||
"next": "13.4.12",
|
||||
"next": "14.0.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-ga4": "^2.1.0",
|
||||
@ -53,7 +50,7 @@
|
||||
"react-simple-typewriter": "^5.0.1",
|
||||
"react-zoomable-ui": "^0.11.0",
|
||||
"reaflow": "5.2.8",
|
||||
"styled-components": "^6.1.1",
|
||||
"styled-components": "^6.1.3",
|
||||
"toml": "^3.0.0",
|
||||
"use-long-press": "^3.1.5",
|
||||
"zustand": "^4.4.7"
|
||||
@ -67,14 +64,14 @@
|
||||
"@types/lodash.get": "^4.4.9",
|
||||
"@types/lodash.set": "^4.3.9",
|
||||
"@types/node": "^20.4.7",
|
||||
"@types/react": "18.2.18",
|
||||
"@types/react": "18.2.45",
|
||||
"eslint": "8.56.0",
|
||||
"eslint-config-next": "13.4.12",
|
||||
"eslint-config-next": "14.0.4",
|
||||
"eslint-config-prettier": "^8.10.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-unused-imports": "^3.0.0",
|
||||
"prettier": "^3.1.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "5.1.6"
|
||||
"typescript": "5.3.3"
|
||||
}
|
||||
}
|
||||
|
5010
pnpm-lock.yaml
generated
Normal file
5010
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -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>;
|
||||
};
|
@ -19,10 +19,10 @@ export const SearchInput: React.FC = () => {
|
||||
onFocus={() => ReactGA.event({ action: "focus_node_search", category: "User" })}
|
||||
placeholder="Search Node"
|
||||
onKeyDown={getHotkeyHandler([["Enter", skip]])}
|
||||
icon={<AiOutlineSearch />}
|
||||
leftSection={<AiOutlineSearch />}
|
||||
rightSection={
|
||||
<Flex h={30} align="center" gap="sm">
|
||||
<Text size="xs" color="dimmed">
|
||||
<Text size="xs" c="dimmed">
|
||||
{searchValue && `${nodeCount}/${nodeCount > 0 ? currentNode + 1 : "0"}`}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
@ -2,34 +2,24 @@ import { createGlobalStyle } from "styled-components";
|
||||
|
||||
const GlobalStyle = createGlobalStyle`
|
||||
html, body {
|
||||
color: ${({ theme }) => theme.FULL_WHITE} !important;
|
||||
font-weight: 400;
|
||||
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;
|
||||
}
|
||||
background: rgb(246,249,253);
|
||||
background: radial-gradient(circle, rgb(245 245 245) 33%, rgb(252 252 255) 100%);
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
scroll-behavior: smooth !important;
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
scroll-behavior: smooth !important;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* g {
|
||||
opacity: 1 !important;
|
||||
} */
|
||||
|
||||
.mantine-Modal-inner {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
svg {
|
||||
vertical-align: text-top;
|
||||
}
|
||||
@ -48,92 +38,6 @@ const GlobalStyle = createGlobalStyle`
|
||||
padding: 0;
|
||||
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;
|
||||
|
@ -83,15 +83,15 @@ export const darkTheme = {
|
||||
BACKGROUND_NODE: "#2B2C3E",
|
||||
BACKGROUND_TERTIARY: "#202225",
|
||||
BACKGROUND_SECONDARY: "#2f3136",
|
||||
TOOLBAR_BG: "#2f3136",
|
||||
TOOLBAR_BG: "#262626",
|
||||
BACKGROUND_PRIMARY: "#36393f",
|
||||
BACKGROUND_MODIFIER_ACCENT: "rgba(79,84,92,0.48)",
|
||||
MODAL_BACKGROUND: "#36393E",
|
||||
TEXT_NORMAL: "#dcddde",
|
||||
TEXT_POSITIVE: "hsl(139,calc(var(--saturation-factor, 1)*51.6%),52.2%)",
|
||||
GRID_BG_COLOR: "#1C1C1C",
|
||||
GRID_COLOR_PRIMARY: "#2A2A2A",
|
||||
GRID_COLOR_SECONDARY: "#252525",
|
||||
GRID_BG_COLOR: "#1E1E1E",
|
||||
GRID_COLOR_PRIMARY: "#272626",
|
||||
GRID_COLOR_SECONDARY: "#232323",
|
||||
};
|
||||
|
||||
export const lightTheme = {
|
||||
@ -110,7 +110,7 @@ export const lightTheme = {
|
||||
BACKGROUND_NODE: "#F6F8FA",
|
||||
BACKGROUND_TERTIARY: "#e3e5e8",
|
||||
BACKGROUND_SECONDARY: "#f2f3f5",
|
||||
TOOLBAR_BG: "#FAFAFA",
|
||||
TOOLBAR_BG: "#ECECEC",
|
||||
BACKGROUND_PRIMARY: "#FFFFFF",
|
||||
BACKGROUND_MODIFIER_ACCENT: "rgba(106,116,128,0.24)",
|
||||
MODAL_BACKGROUND: "#FFFFFF",
|
||||
|
@ -28,7 +28,7 @@ const StyledBottomBar = styled.div`
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-top: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT};
|
||||
background: ${({ theme }) => theme.BACKGROUND_TERTIARY};
|
||||
background: ${({ theme }) => theme.TOOLBAR_BG};
|
||||
max-height: 27px;
|
||||
height: 27px;
|
||||
z-index: 35;
|
||||
@ -204,19 +204,23 @@ export const BottomBar = () => {
|
||||
<Popover.Target>
|
||||
<Flex align="center" gap={2}>
|
||||
<VscError color="red" size={16} />
|
||||
<Text color="red" fw="bold">
|
||||
<Text c="red" fw={500} fz="xs">
|
||||
Invalid
|
||||
</Text>
|
||||
</Flex>
|
||||
</Popover.Target>
|
||||
<Popover.Dropdown sx={{ pointerEvents: "none" }}>
|
||||
<Popover.Dropdown
|
||||
style={{
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
>
|
||||
<Text size="xs">{error}</Text>
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
) : (
|
||||
<Flex align="center" gap={2}>
|
||||
<MdOutlineCheckCircleOutline />
|
||||
<Text>Valid</Text>
|
||||
<Text size="xs">Valid</Text>
|
||||
</Flex>
|
||||
)}
|
||||
</StyledBottomBarItem>
|
||||
@ -242,12 +246,12 @@ export const BottomBar = () => {
|
||||
{liveTransformEnabled ? (
|
||||
<StyledBottomBarItem onClick={() => toggleLiveTransform(false)}>
|
||||
<VscSync />
|
||||
<Text>Live Transform</Text>
|
||||
<Text fz="xs">Live Transform</Text>
|
||||
</StyledBottomBarItem>
|
||||
) : (
|
||||
<StyledBottomBarItem onClick={() => toggleLiveTransform(true)}>
|
||||
<VscSyncIgnored />
|
||||
<Text>Manual Transform</Text>
|
||||
<Text fz="xs">Manual Transform</Text>
|
||||
</StyledBottomBarItem>
|
||||
)}
|
||||
{!liveTransformEnabled && (
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
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 { toast } from "react-hot-toast";
|
||||
import { GoDependabot } from "react-icons/go";
|
||||
@ -23,7 +23,7 @@ function removeWhitespaces(inputString: string) {
|
||||
return compactString;
|
||||
}
|
||||
|
||||
const StyledPromptInput = styled(Input)`
|
||||
const StyledPromptInput = styled(TextInput)`
|
||||
.mantine-Input-input {
|
||||
font-weight: 500;
|
||||
background: ${({ theme }) => theme.PROMPT_BG};
|
||||
@ -112,7 +112,7 @@ const PromptInput = () => {
|
||||
</ActionIcon>
|
||||
</>
|
||||
}
|
||||
icon={completing ? <Loader size="xs" /> : <GoDependabot strokeWidth={1} />}
|
||||
leftSection={completing ? <Loader size="xs" /> : <GoDependabot strokeWidth={1} />}
|
||||
radius={0}
|
||||
/>
|
||||
</div>
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
@ -27,7 +27,7 @@ export const AccountModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
return (
|
||||
<Modal title={`Hello, ${user?.user_metadata.name}!`} opened={opened} onClose={onClose} centered>
|
||||
<Paper p="md">
|
||||
<Group noWrap>
|
||||
<Group>
|
||||
<Avatar src={user?.user_metadata.avatar_url} size={94}>
|
||||
JC
|
||||
</Avatar>
|
||||
@ -36,13 +36,13 @@ export const AccountModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
{user?.user_metadata.name}
|
||||
</Text>
|
||||
|
||||
<Group noWrap spacing={10} mt={3}>
|
||||
<Group gap={10} mt={3}>
|
||||
<Text fz="xs" c="dimmed">
|
||||
{user?.email}
|
||||
</Text>
|
||||
</Group>
|
||||
|
||||
<Group noWrap spacing={10} mt={5}>
|
||||
<Group gap={10} mt={5}>
|
||||
<Text fz="xs" c="dimmed">
|
||||
<Badge
|
||||
size="sm"
|
||||
@ -60,7 +60,7 @@ export const AccountModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
</Paper>
|
||||
|
||||
<Divider py="xs" />
|
||||
<Group position="right">
|
||||
<Group justify="right">
|
||||
{isPremium && !premiumCancelled ? (
|
||||
<Button
|
||||
variant="light"
|
||||
@ -77,7 +77,7 @@ export const AccountModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
<Button
|
||||
variant="gradient"
|
||||
gradient={{ from: "teal", to: "lime", deg: 105 }}
|
||||
leftIcon={<IoRocketSharp />}
|
||||
leftSection={<IoRocketSharp />}
|
||||
onClick={() => setVisible("premium")(true)}
|
||||
>
|
||||
UPGRADE TO PREMIUM!
|
||||
|
@ -44,22 +44,22 @@ export const CancelPremiumModal: React.FC<ModalProps> = ({ opened, onClose }) =>
|
||||
|
||||
return (
|
||||
<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">
|
||||
Cancellation will take effect at the end of your current billing period.
|
||||
<br />
|
||||
<br />
|
||||
You can restart your subscription anytime.
|
||||
<br />
|
||||
<Text size="xs" color="dimmed">
|
||||
If you have problems with cancelling plan please contact: contact@jsoncrack.com
|
||||
</Text>
|
||||
<Anchor target="_blank" href="https://patreon.com/herowand">
|
||||
<Anchor fz="xs" target="_blank" href="https://patreon.com/herowand">
|
||||
Click here to cancel if you are Patreon member
|
||||
</Anchor>
|
||||
<Text size="xs" c="dimmed" mt="lg">
|
||||
If you have problems with cancelling plan please contact: contact@jsoncrack.com
|
||||
</Text>
|
||||
</Text>
|
||||
<Divider py="xs" />
|
||||
<Group position="right">
|
||||
<Group justify="right">
|
||||
<Button color="dark" variant="subtle" onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
|
@ -24,7 +24,7 @@ export const ClearModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
<Text>Are you sure you want to delete JSON?</Text>
|
||||
</Group>
|
||||
<Divider py="xs" />
|
||||
<Group position="right">
|
||||
<Group justify="right">
|
||||
<Button color="red" onClick={handleClear}>
|
||||
Confirm
|
||||
</Button>
|
||||
|
@ -74,7 +74,7 @@ const UpdateNameModal: React.FC<{
|
||||
placeholder={file?.name}
|
||||
onChange={e => setName(e.currentTarget.value)}
|
||||
/>
|
||||
<Group position="right">
|
||||
<Group justify="right">
|
||||
<Button variant="outline" onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
@ -144,48 +144,63 @@ export const CloudModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
const rows = React.useMemo(
|
||||
() =>
|
||||
data?.map(element => (
|
||||
<tr key={element.id}>
|
||||
<td>
|
||||
<Table.Tr key={element.id}>
|
||||
<Table.Td>
|
||||
<Flex align="center" gap="xs">
|
||||
{element.private ? <VscLock /> : <VscUnlock />}
|
||||
{element.id}
|
||||
</Flex>
|
||||
</td>
|
||||
<td>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Flex align="center" justify="space-between">
|
||||
{element.name}
|
||||
<ActionIcon color="cyan" onClick={() => setCurrentFile(element)}>
|
||||
<ActionIcon
|
||||
variant="transparent"
|
||||
color="cyan"
|
||||
onClick={() => setCurrentFile(element)}
|
||||
>
|
||||
<VscEdit />
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
</td>
|
||||
<td>{dayjs(element.created_at).format("DD.MM.YYYY")}</td>
|
||||
<td>
|
||||
</Table.Td>
|
||||
<Table.Td>{dayjs(element.created_at).format("DD.MM.YYYY")}</Table.Td>
|
||||
<Table.Td>
|
||||
<Badge variant="light" color={colorByFormat[element.format]} size="sm">
|
||||
{element.format.toUpperCase()}
|
||||
</Badge>
|
||||
</td>
|
||||
<td>{element.views.toLocaleString("en-US")}</td>
|
||||
<td>
|
||||
</Table.Td>
|
||||
<Table.Td>{element.views.toLocaleString("en-US")}</Table.Td>
|
||||
<Table.Td>
|
||||
<Flex gap="xs">
|
||||
<ActionIcon
|
||||
component={Link}
|
||||
href={`?json=${element.id}`}
|
||||
prefetch={false}
|
||||
color="blue"
|
||||
onClick={onClose}
|
||||
>
|
||||
<MdFileOpen size="18" />
|
||||
</ActionIcon>
|
||||
<ActionIcon color="red" onClick={() => onDeleteClick(element)}>
|
||||
<FaTrash size="18" />
|
||||
</ActionIcon>
|
||||
<ActionIcon color="green" onClick={() => copyShareLink(element.id)}>
|
||||
<AiOutlineLink />
|
||||
</ActionIcon>
|
||||
<ActionIcon.Group>
|
||||
<ActionIcon
|
||||
variant="transparent"
|
||||
component={Link}
|
||||
href={`?json=${element.id}`}
|
||||
prefetch={false}
|
||||
color="blue"
|
||||
onClick={onClose}
|
||||
>
|
||||
<MdFileOpen size="18" />
|
||||
</ActionIcon>
|
||||
<ActionIcon
|
||||
variant="transparent"
|
||||
color="red"
|
||||
onClick={() => onDeleteClick(element)}
|
||||
>
|
||||
<FaTrash size="18" />
|
||||
</ActionIcon>
|
||||
<ActionIcon
|
||||
variant="transparent"
|
||||
color="green"
|
||||
onClick={() => copyShareLink(element.id)}
|
||||
>
|
||||
<AiOutlineLink />
|
||||
</ActionIcon>
|
||||
</ActionIcon.Group>
|
||||
</Flex>
|
||||
</td>
|
||||
</tr>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
)),
|
||||
[data, copyShareLink, onClose, onDeleteClick]
|
||||
);
|
||||
@ -214,10 +229,10 @@ export const CloudModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
]}
|
||||
/>
|
||||
<div>
|
||||
<Text color="dimmed" size="xs" transform="uppercase" weight={700}>
|
||||
<Text c="dimmed" fz="xs" fw={700} style={{ textTransform: "uppercase" }}>
|
||||
Total Quota
|
||||
</Text>
|
||||
<Text weight={700} size="lg">
|
||||
<Text fw={700} size="lg">
|
||||
{data.length} / {totalQuota}
|
||||
</Text>
|
||||
</div>
|
||||
@ -231,11 +246,19 @@ export const CloudModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
onClick={onCreate}
|
||||
disabled={isCreateDisabled}
|
||||
>
|
||||
<Text fz="md" align="center" color="blue">
|
||||
<Flex align="center" justify="center">
|
||||
<VscAdd size="24" />
|
||||
Create New Document
|
||||
</Flex>
|
||||
<Text
|
||||
fz="sm"
|
||||
fw="bold"
|
||||
c="blue"
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<VscAdd size="18" strokeWidth={1} />
|
||||
Create New Document
|
||||
</Text>
|
||||
</UnstyledButton>
|
||||
</Paper>
|
||||
@ -248,18 +271,18 @@ export const CloudModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
<Divider py="xs" />
|
||||
<Paper>
|
||||
<ScrollArea h="100%" offsetScrollbars>
|
||||
<Table fontSize="xs" verticalSpacing="xs" striped withBorder>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Create Date</th>
|
||||
<th>Format</th>
|
||||
<th>Views</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{rows}</tbody>
|
||||
<Table fz="xs" verticalSpacing="xs" striped withTableBorder>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>ID</Table.Th>
|
||||
<Table.Th>Name</Table.Th>
|
||||
<Table.Th>Create Date</Table.Th>
|
||||
<Table.Th>Format</Table.Th>
|
||||
<Table.Th>Views</Table.Th>
|
||||
<Table.Th>Actions</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>{rows}</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</Paper>
|
||||
|
@ -7,10 +7,8 @@ import {
|
||||
Modal,
|
||||
Button,
|
||||
Divider,
|
||||
Grid,
|
||||
ModalProps,
|
||||
ColorInput,
|
||||
Stack,
|
||||
} from "@mantine/core";
|
||||
import { toBlob, toJpeg, toPng, toSvg } from "html-to-image";
|
||||
import ReactGA from "react-ga4";
|
||||
@ -127,49 +125,44 @@ export const DownloadModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
|
||||
return (
|
||||
<Modal opened={opened} onClose={onClose} title="Download Image" centered>
|
||||
<Grid py="sm" align="end" grow>
|
||||
<Grid.Col span={8}>
|
||||
<TextInput
|
||||
label="File Name"
|
||||
value={fileDetails.filename}
|
||||
onChange={e => updateDetails("filename", e.target.value)}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={4}>
|
||||
<SegmentedControl
|
||||
value={extension}
|
||||
onChange={e => setExtension(e as Extensions)}
|
||||
fullWidth
|
||||
data={[
|
||||
{ label: "SVG", value: Extensions.SVG },
|
||||
{ label: "PNG", value: Extensions.PNG },
|
||||
{ label: "JPEG", value: Extensions.JPEG },
|
||||
]}
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
<Stack py="sm">
|
||||
<ColorInput
|
||||
label="Background Color"
|
||||
value={fileDetails.backgroundColor}
|
||||
onChange={color => updateDetails("backgroundColor", color)}
|
||||
withEyeDropper={false}
|
||||
/>
|
||||
<ColorPicker
|
||||
format="rgba"
|
||||
value={fileDetails.backgroundColor}
|
||||
onChange={color => updateDetails("backgroundColor", color)}
|
||||
swatches={swatches}
|
||||
withPicker={false}
|
||||
fullWidth
|
||||
/>
|
||||
</Stack>
|
||||
<Divider py="xs" />
|
||||
<Group position="right">
|
||||
<Button leftIcon={<FiCopy />} onClick={clipboardImage}>
|
||||
<TextInput
|
||||
label="File Name"
|
||||
value={fileDetails.filename}
|
||||
onChange={e => updateDetails("filename", e.target.value)}
|
||||
mb="lg"
|
||||
/>
|
||||
<SegmentedControl
|
||||
value={extension}
|
||||
onChange={e => setExtension(e as Extensions)}
|
||||
fullWidth
|
||||
data={[
|
||||
{ label: "SVG", value: Extensions.SVG },
|
||||
{ label: "PNG", value: Extensions.PNG },
|
||||
{ label: "JPEG", value: Extensions.JPEG },
|
||||
]}
|
||||
mb="lg"
|
||||
/>
|
||||
<ColorInput
|
||||
label="Background Color"
|
||||
value={fileDetails.backgroundColor}
|
||||
onChange={color => updateDetails("backgroundColor", color)}
|
||||
withEyeDropper={false}
|
||||
mb="lg"
|
||||
/>
|
||||
<ColorPicker
|
||||
format="rgba"
|
||||
value={fileDetails.backgroundColor}
|
||||
onChange={color => updateDetails("backgroundColor", color)}
|
||||
swatches={swatches}
|
||||
withPicker={false}
|
||||
fullWidth
|
||||
/>
|
||||
<Divider my="lg" />
|
||||
<Group justify="right">
|
||||
<Button leftSection={<FiCopy />} onClick={clipboardImage}>
|
||||
Clipboard
|
||||
</Button>
|
||||
<Button color="green" leftIcon={<FiDownload />} onClick={exportAsImage}>
|
||||
<Button color="green" leftSection={<FiDownload />} onClick={exportAsImage}>
|
||||
Download
|
||||
</Button>
|
||||
</Group>
|
||||
|
@ -10,7 +10,7 @@ const StyledUploadWrapper = styled.label`
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: ${({ theme }) => theme.BACKGROUND_SECONDARY};
|
||||
background: ${({ theme }) => theme.GRID_BG_COLOR};
|
||||
border: 2px dashed ${({ theme }) => theme.BACKGROUND_TERTIARY};
|
||||
border-radius: 5px;
|
||||
min-height: 200px;
|
||||
@ -102,7 +102,7 @@ export const ImportModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
</StyledUploadWrapper>
|
||||
</Stack>
|
||||
<Divider py="xs" />
|
||||
<Group position="right">
|
||||
<Group justify="right">
|
||||
<Button onClick={handleImportFile} disabled={!(jsonFile || url)}>
|
||||
Import
|
||||
</Button>
|
||||
|
@ -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>
|
||||
</Group>
|
||||
</Stack>
|
||||
|
@ -1,9 +1,7 @@
|
||||
import React from "react";
|
||||
import { CodeHighlight } from "@mantine/code-highlight";
|
||||
import { Modal, Stack, Text, ScrollArea, ModalProps, Button } from "@mantine/core";
|
||||
import { Prism } from "@mantine/prism";
|
||||
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 { isIframe } from "src/lib/utils/widget";
|
||||
import useConfig from "src/store/useConfig";
|
||||
@ -22,36 +20,13 @@ const dataToString = (data: any) => {
|
||||
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 }) => {
|
||||
const isPremium = useUser(state => state.premium);
|
||||
const editContents = useFile(state => state.editContents);
|
||||
const setVisible = useModal(state => state.setVisible);
|
||||
const darkmodeEnabled = useConfig(state => (state.darkmodeEnabled ? "vs-dark" : "light"));
|
||||
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 [editMode, setEditMode] = React.useState(false);
|
||||
const [value, setValue] = React.useState(nodeData || "");
|
||||
@ -83,8 +58,8 @@ export const NodeModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
|
||||
return (
|
||||
<Modal title="Node Content" size="auto" opened={opened} onClose={onModalClose} centered>
|
||||
<Stack py="sm" spacing="sm">
|
||||
<Stack spacing="xs">
|
||||
<Stack py="sm" gap="sm">
|
||||
<Stack gap="xs">
|
||||
<Text fz="sm" fw={700}>
|
||||
Content
|
||||
</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>
|
||||
{isEditVisible && (
|
||||
<Stack spacing="xs">
|
||||
<Stack gap="xs">
|
||||
{editMode ? (
|
||||
<Button
|
||||
variant={value ? "filled" : "light"}
|
||||
@ -117,7 +101,11 @@ export const NodeModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
{value.length ? "Update Document" : "Cancel"}
|
||||
</Button>
|
||||
) : (
|
||||
<Button onClick={onEditClick} leftIcon={!isPremium && <VscLock />} variant="filled">
|
||||
<Button
|
||||
onClick={onEditClick}
|
||||
leftSection={!isPremium && <VscLock />}
|
||||
variant="filled"
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
)}
|
||||
@ -126,7 +114,17 @@ export const NodeModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
<Text fz="sm" fw={700}>
|
||||
Node Path
|
||||
</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>
|
||||
</Modal>
|
||||
);
|
||||
|
@ -17,10 +17,10 @@ export const PremiumModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
return (
|
||||
<Modal title="Your Plan" size="auto" opened={opened} onClose={onClose} centered zIndex={202}>
|
||||
<Flex gap="lg">
|
||||
<Stack spacing="xs">
|
||||
<Stack gap="xs">
|
||||
<Title order={3}>
|
||||
Free plan
|
||||
<Text size="sm" color="dimmed">
|
||||
<Text size="sm" c="dimmed">
|
||||
(Free)
|
||||
</Text>
|
||||
</Title>
|
||||
@ -42,10 +42,10 @@ export const PremiumModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
</List>
|
||||
</Stack>
|
||||
<Divider color="gray" orientation="vertical" />
|
||||
<Stack spacing="xs">
|
||||
<Stack gap="xs">
|
||||
<Title order={3}>
|
||||
JSON Crack Plus
|
||||
<Text size="sm" color="dimmed">
|
||||
<Text size="sm" c="dimmed">
|
||||
USD 7$/mo
|
||||
</Text>
|
||||
</Title>
|
||||
|
@ -32,7 +32,7 @@ export const ReviewModal: React.FC<ModalProps> = ({ opened, 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" />
|
||||
<Textarea
|
||||
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}
|
||||
minRows={5}
|
||||
/>
|
||||
<Text align="right" size={12} color="dimmed">
|
||||
<Text fz={12} c="dimmed" style={{ textAlign: "right" }}>
|
||||
500/{review.length}
|
||||
</Text>
|
||||
<Text size={12}>
|
||||
<Text fz={12}>
|
||||
* Your feedback is kept anonymous. If you wish to be contacted, please provide your email
|
||||
address along with your feedback.
|
||||
</Text>
|
||||
|
@ -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}>
|
||||
Clear
|
||||
</Button>
|
||||
<Button onClick={onApply} disabled={!schema} rightIcon={!isPremium && <VscLock />}>
|
||||
<Button onClick={onApply} disabled={!schema} rightSection={!isPremium && <VscLock />}>
|
||||
Apply
|
||||
</Button>
|
||||
</Group>
|
||||
|
@ -48,7 +48,7 @@ export const ShareModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
color="green"
|
||||
target="_blank"
|
||||
href="/docs"
|
||||
leftIcon={<FiExternalLink />}
|
||||
leftSection={<FiExternalLink />}
|
||||
fullWidth
|
||||
>
|
||||
Learn How to Embed
|
||||
|
@ -1,8 +1,6 @@
|
||||
import React from "react";
|
||||
import { CodeHighlight } from "@mantine/code-highlight";
|
||||
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";
|
||||
|
||||
enum Language {
|
||||
@ -53,17 +51,14 @@ export const TypeModal: React.FC<ModalProps> = ({ opened, onClose }) => {
|
||||
onChange={e => setSelectedType(e as Language)}
|
||||
/>
|
||||
<ScrollArea>
|
||||
<Prism
|
||||
<CodeHighlight
|
||||
miw={350}
|
||||
mah={600}
|
||||
language={selectedType}
|
||||
copyLabel="Copy to clipboard"
|
||||
copiedLabel="Copied to clipboard"
|
||||
withLineNumbers
|
||||
getPrismTheme={(_theme, colorScheme) => (colorScheme === "light" ? vsLight : vsDark)}
|
||||
>
|
||||
{type}
|
||||
</Prism>
|
||||
code={type}
|
||||
/>
|
||||
</ScrollArea>
|
||||
</Stack>
|
||||
</Modal>
|
||||
|
@ -32,7 +32,9 @@ export const AccountMenu = () => {
|
||||
<Menu.Dropdown>
|
||||
{user ? (
|
||||
<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)}
|
||||
closeMenuOnClick
|
||||
>
|
||||
@ -40,14 +42,14 @@ export const AccountMenu = () => {
|
||||
</Menu.Item>
|
||||
) : (
|
||||
<Link href="/sign-in">
|
||||
<Menu.Item icon={<VscSignIn />}>
|
||||
<Menu.Item leftSection={<VscSignIn />}>
|
||||
<Text size="xs">Sign in</Text>
|
||||
</Menu.Item>
|
||||
</Link>
|
||||
)}
|
||||
{!premium && (
|
||||
<Menu.Item
|
||||
icon={<MdOutlineWorkspacePremium color="red" />}
|
||||
leftSection={<MdOutlineWorkspacePremium color="red" />}
|
||||
onClick={() => setVisible("premium")(true)}
|
||||
closeMenuOnClick
|
||||
>
|
||||
@ -60,13 +62,13 @@ export const AccountMenu = () => {
|
||||
<>
|
||||
<Menu.Divider />
|
||||
<Menu.Item
|
||||
icon={<VscFeedback />}
|
||||
leftSection={<VscFeedback />}
|
||||
onClick={() => setVisible("review")(true)}
|
||||
closeMenuOnClick
|
||||
>
|
||||
<Text size="xs">Feedback</Text>
|
||||
</Menu.Item>
|
||||
<Menu.Item icon={<VscSignOut />} onClick={() => logout()} closeMenuOnClick>
|
||||
<Menu.Item leftSection={<VscSignOut />} onClick={() => logout()} closeMenuOnClick>
|
||||
<Text size="xs">Log out</Text>
|
||||
</Menu.Item>
|
||||
</>
|
||||
|
@ -5,7 +5,7 @@ import { isIframe } from "src/lib/utils/widget";
|
||||
import * as Styles from "./styles";
|
||||
|
||||
export const Logo = () => {
|
||||
const [logoURL, setLogoURL] = React.useState("CTRL");
|
||||
const [logoURL, setLogoURL] = React.useState("");
|
||||
|
||||
React.useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
@ -17,6 +17,8 @@ export const Logo = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (!logoURL) return null;
|
||||
|
||||
return (
|
||||
<Styles.StyledToolElement title="JSON Crack">
|
||||
<Flex gap="xs" align="center" justify="center">
|
||||
|
@ -31,37 +31,37 @@ export const OptionsMenu = () => {
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item
|
||||
icon={<BsCheck2 opacity={rulersEnabled ? 100 : 0} />}
|
||||
leftSection={<BsCheck2 opacity={rulersEnabled ? 100 : 0} />}
|
||||
onClick={() => toggleRulers(!rulersEnabled)}
|
||||
>
|
||||
<Text size="xs">Rulers</Text>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
icon={<BsCheck2 opacity={gesturesEnabled ? 100 : 0} />}
|
||||
leftSection={<BsCheck2 opacity={gesturesEnabled ? 100 : 0} />}
|
||||
onClick={() => toggleGestures(!gesturesEnabled)}
|
||||
>
|
||||
<Text size="xs">Trackpad Gestures</Text>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
icon={<BsCheck2 opacity={childrenCountVisible ? 100 : 0} />}
|
||||
leftSection={<BsCheck2 opacity={childrenCountVisible ? 100 : 0} />}
|
||||
onClick={() => toggleChildrenCount(!childrenCountVisible)}
|
||||
>
|
||||
<Text size="xs">Item Count</Text>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
icon={<BsCheck2 opacity={imagePreviewEnabled ? 100 : 0} />}
|
||||
leftSection={<BsCheck2 opacity={imagePreviewEnabled ? 100 : 0} />}
|
||||
onClick={() => toggleImagePreview(!imagePreviewEnabled)}
|
||||
>
|
||||
<Text size="xs">Image Link Preview</Text>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
icon={<BsCheck2 opacity={collapseButtonVisible ? 100 : 0} />}
|
||||
leftSection={<BsCheck2 opacity={collapseButtonVisible ? 100 : 0} />}
|
||||
onClick={() => toggleCollapseButton(!collapseButtonVisible)}
|
||||
>
|
||||
<Text size="xs">Show Expand/Collapse</Text>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
icon={<BsCheck2 opacity={darkmodeEnabled ? 100 : 0} />}
|
||||
leftSection={<BsCheck2 opacity={darkmodeEnabled ? 100 : 0} />}
|
||||
onClick={() => toggleDarkMode(!darkmodeEnabled)}
|
||||
>
|
||||
<Text size="xs">Dark Mode</Text>
|
||||
|
@ -19,16 +19,24 @@ export const ToolsMenu = () => {
|
||||
</Styles.StyledToolElement>
|
||||
</Menu.Target>
|
||||
<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)
|
||||
</Menu.Item>
|
||||
<Menu.Item fz={12} icon={<VscJson />} onClick={() => setVisible("schema")(true)}>
|
||||
<Menu.Item fz={12} leftSection={<VscJson />} onClick={() => setVisible("schema")(true)}>
|
||||
JSON Schema
|
||||
</Menu.Item>
|
||||
<Menu.Item fz={12} icon={<SiJsonwebtokens />} onClick={() => setVisible("jwt")(true)}>
|
||||
<Menu.Item
|
||||
fz={12}
|
||||
leftSection={<SiJsonwebtokens />}
|
||||
onClick={() => setVisible("jwt")(true)}
|
||||
>
|
||||
Decode JWT
|
||||
</Menu.Item>
|
||||
<Menu.Item fz={12} icon={<VscGroupByRefType />} onClick={() => setVisible("type")(true)}>
|
||||
<Menu.Item
|
||||
fz={12}
|
||||
leftSection={<VscGroupByRefType />}
|
||||
onClick={() => setVisible("type")(true)}
|
||||
>
|
||||
Generate Type
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
|
@ -86,9 +86,9 @@ export const ViewMenu = () => {
|
||||
label: "Tools",
|
||||
});
|
||||
}}
|
||||
icon={<Styles.StyledFlowIcon rotate={rotateLayout(direction || "RIGHT")} />}
|
||||
leftSection={<Styles.StyledFlowIcon rotate={rotateLayout(direction || "RIGHT")} />}
|
||||
rightSection={
|
||||
<Text ml="md" fz={10} color="dimmed">
|
||||
<Text ml="md" fz={10} c="dimmed">
|
||||
{coreKey} Shift D
|
||||
</Text>
|
||||
}
|
||||
@ -105,9 +105,9 @@ export const ViewMenu = () => {
|
||||
label: "Tools",
|
||||
});
|
||||
}}
|
||||
icon={foldNodes ? <CgArrowsShrinkH /> : <CgArrowsMergeAltH />}
|
||||
leftSection={foldNodes ? <CgArrowsShrinkH /> : <CgArrowsMergeAltH />}
|
||||
rightSection={
|
||||
<Text ml="md" fz={10} color="dimmed">
|
||||
<Text ml="md" fz={10} c="dimmed">
|
||||
{coreKey} Shift F
|
||||
</Text>
|
||||
}
|
||||
@ -124,16 +124,16 @@ export const ViewMenu = () => {
|
||||
label: "Tools",
|
||||
});
|
||||
}}
|
||||
icon={graphCollapsed ? <VscExpandAll /> : <VscCollapseAll />}
|
||||
leftSection={graphCollapsed ? <VscExpandAll /> : <VscCollapseAll />}
|
||||
rightSection={
|
||||
<Text ml="md" fz={10} color="dimmed">
|
||||
<Text ml="md" fz={10} c="dimmed">
|
||||
{coreKey} Shift C
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
{graphCollapsed ? "Expand" : "Collapse"} Nodes
|
||||
</Menu.Item>
|
||||
<Menu.Item fz={12} onClick={focusFirstNode} icon={<VscTarget />}>
|
||||
<Menu.Item fz={12} onClick={focusFirstNode} leftSection={<VscTarget />}>
|
||||
Focus to First Node
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
|
@ -21,7 +21,7 @@ export const ViewModeMenu = () => {
|
||||
<Menu.Dropdown>
|
||||
<SegmentedControl
|
||||
value={viewMode}
|
||||
onChange={setViewMode}
|
||||
onChange={e => setViewMode(e as ViewMode)}
|
||||
data={[
|
||||
{ value: ViewMode.Graph, label: "Graph" },
|
||||
{ value: ViewMode.Tree, label: "Tree" },
|
||||
|
@ -27,7 +27,7 @@ export const ZoomMenu = () => {
|
||||
<Menu shadow="md" trigger="click" closeOnItemClick={false} withArrow>
|
||||
<Menu.Target>
|
||||
<Styles.StyledToolElement>
|
||||
<Flex gap={4}>
|
||||
<Flex gap={4} align="center">
|
||||
{Math.round(zoomFactor * 100)}%
|
||||
<CgChevronDown />
|
||||
</Flex>
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 { AiOutlineFullscreen } from "react-icons/ai";
|
||||
import { FiDownload } from "react-icons/fi";
|
||||
@ -48,49 +48,44 @@ export const Toolbar: React.FC<{ isWidget?: boolean }> = ({ isWidget = false })
|
||||
<Styles.StyledTools>
|
||||
{isWidget && <Logo />}
|
||||
{!isWidget && (
|
||||
<MediaQuery smallerThan="xs" styles={{ display: "none" }}>
|
||||
<Group spacing="xs" position="left" w="100%" noWrap>
|
||||
<Styles.StyledToolElement title="JSON Crack">
|
||||
<Flex gap="xs" align="center" justify="center">
|
||||
<JSONCrackLogo fontSize="1.2em" />
|
||||
</Flex>
|
||||
</Styles.StyledToolElement>
|
||||
<Group gap="xs" justify="left" w="100%" style={{ flexWrap: "nowrap" }}>
|
||||
<Styles.StyledToolElement title="JSON Crack">
|
||||
<Flex gap="xs" align="center" justify="center">
|
||||
<JSONCrackLogo fontSize="1.2em" />
|
||||
</Flex>
|
||||
</Styles.StyledToolElement>
|
||||
|
||||
<Select
|
||||
defaultValue="json"
|
||||
size="xs"
|
||||
value={format}
|
||||
onChange={setFormat}
|
||||
miw={80}
|
||||
w={120}
|
||||
data={[
|
||||
{ value: FileFormat.JSON, label: "JSON" },
|
||||
{ value: FileFormat.YAML, label: "YAML" },
|
||||
{ value: FileFormat.XML, label: "XML" },
|
||||
{ value: FileFormat.TOML, label: "TOML" },
|
||||
{ value: FileFormat.CSV, label: "CSV" },
|
||||
]}
|
||||
/>
|
||||
<Select
|
||||
defaultValue="json"
|
||||
size="xs"
|
||||
value={format}
|
||||
onChange={e => setFormat(e as FileFormat)}
|
||||
miw={80}
|
||||
w={120}
|
||||
data={[
|
||||
{ value: FileFormat.JSON, label: "JSON" },
|
||||
{ value: FileFormat.YAML, label: "YAML" },
|
||||
{ value: FileFormat.XML, label: "XML" },
|
||||
{ value: FileFormat.TOML, label: "TOML" },
|
||||
{ value: FileFormat.CSV, label: "CSV" },
|
||||
]}
|
||||
/>
|
||||
|
||||
<ViewModeMenu />
|
||||
<Styles.StyledToolElement
|
||||
title="Import File"
|
||||
onClick={() => setVisible("import")(true)}
|
||||
>
|
||||
Import
|
||||
</Styles.StyledToolElement>
|
||||
<ViewMenu />
|
||||
<ToolsMenu />
|
||||
<Styles.StyledToolElement title="Cloud" onClick={() => setVisible("cloud")(true)}>
|
||||
Cloud
|
||||
</Styles.StyledToolElement>
|
||||
<Styles.StyledToolElement title="Download as File" onClick={handleSave}>
|
||||
Download
|
||||
</Styles.StyledToolElement>
|
||||
</Group>
|
||||
</MediaQuery>
|
||||
<ViewModeMenu />
|
||||
<Styles.StyledToolElement title="Import File" onClick={() => setVisible("import")(true)}>
|
||||
Import
|
||||
</Styles.StyledToolElement>
|
||||
<ViewMenu />
|
||||
<ToolsMenu />
|
||||
<Styles.StyledToolElement title="Cloud" onClick={() => setVisible("cloud")(true)}>
|
||||
Cloud
|
||||
</Styles.StyledToolElement>
|
||||
<Styles.StyledToolElement title="Download as File" onClick={handleSave}>
|
||||
Download
|
||||
</Styles.StyledToolElement>
|
||||
</Group>
|
||||
)}
|
||||
<Group spacing="xs" position="right" w="100%" noWrap>
|
||||
<Group gap="xs" justify="right" w="100%" style={{ flexWrap: "nowrap" }}>
|
||||
<SearchInput />
|
||||
{!isWidget && (
|
||||
<>
|
||||
|
@ -4,15 +4,16 @@ import { TiFlowMerge } from "react-icons/ti";
|
||||
export const StyledTools = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
justify-content: space-between;
|
||||
height: 36px;
|
||||
height: 40px;
|
||||
padding: 4px 8px;
|
||||
background: ${({ theme }) => theme.TOOLBAR_BG};
|
||||
color: ${({ theme }) => theme.SILVER};
|
||||
box-shadow: 0 1px 0px ${({ theme }) => theme.BACKGROUND_TERTIARY};
|
||||
z-index: 36;
|
||||
border-bottom: 1px solid ${({ theme }) => theme.SILVER_DARK};
|
||||
|
||||
@media only screen and (max-width: 320px) {
|
||||
display: none;
|
||||
|
@ -159,7 +159,7 @@ const StyledContent = styled.div`
|
||||
export const PremiumView = () => (
|
||||
<StyledPremiumView>
|
||||
<StyledContent>
|
||||
<Title align="center">
|
||||
<Title>
|
||||
<Image width="400" src="assets/icon.png" alt="JSON Crack" />
|
||||
</Title>
|
||||
<StyledInfo>
|
||||
|
@ -1,8 +1,7 @@
|
||||
import React from "react";
|
||||
import { MantineProvider } from "@mantine/core";
|
||||
import { ThemeProvider } from "styled-components";
|
||||
import { MantineProvider, MantineThemeOverride } from "@mantine/core";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { monaSans } from "src/constants/fonts";
|
||||
import { lightTheme, darkTheme } from "src/constants/theme";
|
||||
import useConfig from "src/store/useConfig";
|
||||
|
||||
@ -15,56 +14,14 @@ const queryClient = new QueryClient({
|
||||
},
|
||||
});
|
||||
|
||||
const mantineTheme: MantineThemeOverride = {
|
||||
fontFamily: monaSans.style.fontFamily,
|
||||
headings: {
|
||||
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 }) => {
|
||||
export const EditorWrapper: React.FC<{
|
||||
children: React.ReactNode;
|
||||
}> = ({ children }) => {
|
||||
const darkmodeEnabled = useConfig(state => state.darkmodeEnabled);
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={darkmodeEnabled ? darkTheme : lightTheme}>
|
||||
<MantineProvider
|
||||
theme={{
|
||||
colorScheme: darkmodeEnabled ? "dark" : "light",
|
||||
...mantineTheme,
|
||||
}}
|
||||
withCSSVariables
|
||||
withGlobalStyles
|
||||
withNormalizeCSS
|
||||
>
|
||||
<MantineProvider forceColorScheme={darkmodeEnabled ? "dark" : "light"}>
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
</MantineProvider>
|
||||
</ThemeProvider>
|
||||
|
@ -59,7 +59,7 @@ const ExternalMode = () => {
|
||||
onClick={() => setOpen(true)}
|
||||
color="red"
|
||||
variant="subtle"
|
||||
leftIcon={<VscCode size="1.2rem" />}
|
||||
leftSection={<VscCode size="1.2rem" />}
|
||||
>
|
||||
External Host
|
||||
</Button>
|
||||
@ -68,15 +68,15 @@ const ExternalMode = () => {
|
||||
<StyledTitle>Hi! Did you like the editor?</StyledTitle>
|
||||
<Text>
|
||||
You are currently using the external release of the{" "}
|
||||
<Anchor href="https://jsoncrack.com">JSON Crack</Anchor>. Please consider supporting by
|
||||
one time or monthly sponsorship ✨
|
||||
<Anchor href="https://jsoncrack.com/pricing">JSON Crack</Anchor>. Please consider
|
||||
supporting by buying premium ✨
|
||||
</Text>
|
||||
</Group>
|
||||
<Group pt="lg" position="right">
|
||||
<Group pt="lg" justify="right">
|
||||
<Button
|
||||
onClick={closeModal}
|
||||
component="a"
|
||||
href="https://github.com/sponsors/AykutSarac"
|
||||
href="https://jsoncrack.com/pricing"
|
||||
target="_blank"
|
||||
variant="light"
|
||||
color="red"
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
@ -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",
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
@ -6,18 +6,11 @@ import { monaSans } from "src/constants/fonts";
|
||||
const StyledTitle = styled.div<{ fontSize: string }>`
|
||||
font-weight: 900;
|
||||
margin: 0;
|
||||
color: ${({ theme }) => theme.INTERACTIVE_HOVER};
|
||||
font-family: ${monaSans.style.fontFamily};
|
||||
font-size: ${({ fontSize }) => fontSize};
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
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;
|
||||
z-index: 10;
|
||||
color: ${({ theme }) => theme.TEXT_NORMAL};
|
||||
`;
|
||||
|
||||
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 }) => {
|
||||
return (
|
||||
<Link href="/" prefetch={false} {...props}>
|
||||
<StyledTitle fontSize={fontSize}>
|
||||
<StyledGradientText>JSON</StyledGradientText> CRACK
|
||||
</StyledTitle>
|
||||
</Link>
|
||||
<StyledTitle as={Link} fontSize={fontSize} href="/" prefetch={false} {...props}>
|
||||
JSON CRACK
|
||||
</StyledTitle>
|
||||
);
|
||||
};
|
||||
|
@ -1,14 +1,23 @@
|
||||
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";
|
||||
|
||||
const StyledLayoutWrapper = styled.div`
|
||||
padding-bottom: 48px;
|
||||
`;
|
||||
|
||||
const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
{children}
|
||||
<Footer />
|
||||
</>
|
||||
<MantineProvider forceColorScheme="light">
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<StyledLayoutWrapper>
|
||||
<Navbar />
|
||||
{children}
|
||||
</StyledLayoutWrapper>
|
||||
</ThemeProvider>
|
||||
</MantineProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,25 +1,26 @@
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
import { Button, Menu } from "@mantine/core";
|
||||
import styled from "styled-components";
|
||||
import { Button } from "@mantine/core";
|
||||
import { BiChevronDown } from "react-icons/bi";
|
||||
import useUser from "src/store/useUser";
|
||||
import { JSONCrackLogo } from "../JsonCrackLogo";
|
||||
|
||||
const StyledNavbarWrapper = styled.div`
|
||||
padding: 10px 0;
|
||||
`;
|
||||
const StyledNavbarWrapper = styled.div``;
|
||||
|
||||
const StyledNavbar = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 90vw;
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
margin: 0 auto;
|
||||
border: 2px solid black;
|
||||
background: white;
|
||||
border-bottom: 1px solid gray;
|
||||
background: rgba(255, 255, 255, 0.75);
|
||||
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) {
|
||||
.desktop {
|
||||
@ -53,14 +54,7 @@ export const Navbar = () => {
|
||||
<JSONCrackLogo />
|
||||
</Left>
|
||||
<Middle className="hide-mobile">
|
||||
<Button
|
||||
component={Link}
|
||||
href="/pricing"
|
||||
prefetch={false}
|
||||
variant="subtle"
|
||||
color="dark"
|
||||
radius="md"
|
||||
>
|
||||
<Button component={Link} href="/pricing" variant="subtle" color="dark" radius="md">
|
||||
Pricing
|
||||
</Button>
|
||||
<Button
|
||||
@ -83,6 +77,59 @@ export const Navbar = () => {
|
||||
>
|
||||
Docs
|
||||
</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>
|
||||
<Right>
|
||||
{!isAuthenticated && (
|
||||
@ -90,14 +137,14 @@ export const Navbar = () => {
|
||||
component={Link}
|
||||
href="/sign-in"
|
||||
prefetch={false}
|
||||
variant="light"
|
||||
radius="md"
|
||||
variant="outline"
|
||||
color="grape.9"
|
||||
className="hide-mobile"
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
)}
|
||||
<Button component={Link} href="/editor" prefetch={false} color="pink" radius="md">
|
||||
<Button color="grape.9" component={Link} href="/editor" prefetch={false}>
|
||||
Editor
|
||||
</Button>
|
||||
</Right>
|
||||
|
@ -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-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>
|
||||
);
|
||||
};
|
@ -43,8 +43,8 @@ const NotFound: React.FC = () => {
|
||||
<StyledImageWrapper>
|
||||
<img src="/assets/404.svg" alt="not found" width={300} height={400} />
|
||||
</StyledImageWrapper>
|
||||
<Title color="dark">WIZARDS BEHIND CURTAINS?</Title>
|
||||
<Text color="dark">Looks like you're lost, let's head back to the home!</Text>
|
||||
<Title c="dark">WIZARDS BEHIND CURTAINS?</Title>
|
||||
<Text c="dark">Looks like you're lost, let's head back to the home!</Text>
|
||||
<Button mt="lg" size="lg" type="button" onClick={() => router.push("/")}>
|
||||
Go Home
|
||||
</Button>
|
||||
|
@ -2,8 +2,10 @@ import React from "react";
|
||||
import type { AppProps } from "next/app";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useRouter } from "next/router";
|
||||
import { StyleSheetManager, ThemeProvider } from "styled-components";
|
||||
import { MantineProvider, MantineThemeOverride } from "@mantine/core";
|
||||
import { MantineProvider, createTheme, ColorSchemeScript } 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 ReactGA from "react-ga4";
|
||||
import { monaSans } from "src/constants/fonts";
|
||||
@ -12,6 +14,12 @@ import { lightTheme } from "src/constants/theme";
|
||||
import { supabase } from "src/lib/api/supabase";
|
||||
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 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 ModalController = dynamic(() => import("src/layout/ModalController"));
|
||||
|
||||
const mantineTheme: MantineThemeOverride = {
|
||||
colorScheme: "light",
|
||||
fontFamily: monaSans.style.fontFamily,
|
||||
headings: { fontFamily: monaSans.style.fontFamily },
|
||||
primaryShade: 8,
|
||||
};
|
||||
|
||||
function JsonCrack({
|
||||
Component,
|
||||
pageProps,
|
||||
@ -57,31 +58,29 @@ function JsonCrack({
|
||||
|
||||
return (
|
||||
<SessionContextProvider supabaseClient={supabase}>
|
||||
<StyleSheetManager>
|
||||
<MantineProvider theme={mantineTheme}>
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<GlobalStyle />
|
||||
<MantineProvider theme={mantineTheme} withGlobalStyles withNormalizeCSS withCSSVariables>
|
||||
<Component {...pageProps} />
|
||||
<ModalController />
|
||||
<Toaster
|
||||
position="top-right"
|
||||
containerStyle={{
|
||||
top: 40,
|
||||
right: 6,
|
||||
fontSize: 14,
|
||||
}}
|
||||
toastOptions={{
|
||||
style: {
|
||||
background: "#4D4D4D",
|
||||
color: "#B9BBBE",
|
||||
borderRadius: 4,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<ExternalMode />
|
||||
</MantineProvider>
|
||||
<Component {...pageProps} />
|
||||
<ModalController />
|
||||
<Toaster
|
||||
position="top-right"
|
||||
containerStyle={{
|
||||
top: 40,
|
||||
right: 6,
|
||||
fontSize: 14,
|
||||
}}
|
||||
toastOptions={{
|
||||
style: {
|
||||
background: "#4D4D4D",
|
||||
color: "#B9BBBE",
|
||||
borderRadius: 4,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<ExternalMode />
|
||||
</ThemeProvider>
|
||||
</StyleSheetManager>
|
||||
</MantineProvider>
|
||||
</SessionContextProvider>
|
||||
);
|
||||
}
|
||||
|
@ -7,11 +7,8 @@ import Document, {
|
||||
DocumentInitialProps,
|
||||
} from "next/document";
|
||||
import { ServerStyleSheet } from "styled-components";
|
||||
import { createGetInitialProps } from "@mantine/next";
|
||||
import { SeoTags } from "src/components/SeoTags";
|
||||
|
||||
const getInitialProps = createGetInitialProps();
|
||||
|
||||
class MyDocument extends Document {
|
||||
static async getInitialProps(ctx: DocumentContext): Promise<DocumentInitialProps> {
|
||||
const sheet = new ServerStyleSheet();
|
||||
@ -23,7 +20,7 @@ class MyDocument extends Document {
|
||||
enhanceApp: App => props => sheet.collectStyles(<App {...props} />),
|
||||
});
|
||||
|
||||
const initialProps = await getInitialProps(ctx);
|
||||
const initialProps = await Document.getInitialProps(ctx);
|
||||
|
||||
return {
|
||||
...initialProps,
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React from "react";
|
||||
import Head from "next/head";
|
||||
import styled from "styled-components";
|
||||
import { Group, MediaQuery, Paper, Stack, Text, Title } from "@mantine/core";
|
||||
import { Prism } from "@mantine/prism";
|
||||
import { CodeHighlight } from "@mantine/code-highlight";
|
||||
import { Group, Paper, Stack, Text, Title } from "@mantine/core";
|
||||
import Layout from "src/layout/Layout";
|
||||
|
||||
const StyledFrame = styled.iframe`
|
||||
@ -46,12 +46,12 @@ const Docs = () => {
|
||||
</Head>
|
||||
<Stack mx="auto" maw="75%">
|
||||
<Group mb="lg" mt={40}>
|
||||
<Title order={1} color="dark">
|
||||
<Title order={1} c="dark">
|
||||
Documentation
|
||||
</Title>
|
||||
</Group>
|
||||
<Paper p="md" radius="md" withBorder>
|
||||
<Title order={3} color="dark">
|
||||
<Title order={3} c="dark">
|
||||
# Fetching from URL
|
||||
</Title>
|
||||
<StyledContentBody>
|
||||
@ -81,7 +81,7 @@ const Docs = () => {
|
||||
</StyledContentBody>
|
||||
</Paper>
|
||||
<Paper p="md" radius="md" withBorder>
|
||||
<Title order={2} color="dark">
|
||||
<Title order={2} c="dark">
|
||||
# Embed Saved JSON
|
||||
</Title>
|
||||
<StyledContentBody>
|
||||
@ -102,7 +102,7 @@ const Docs = () => {
|
||||
</StyledContentBody>
|
||||
</Paper>
|
||||
<Paper p="md" radius="md" withBorder>
|
||||
<Title order={2} color="dark">
|
||||
<Title order={2} c="dark">
|
||||
# Communicating with API
|
||||
</Title>
|
||||
<h3>◼︎ Post Message to Embed</h3>
|
||||
@ -118,13 +118,14 @@ const Docs = () => {
|
||||
</StyledHighlight>
|
||||
, you should pass an object consist of "json" and "options" key
|
||||
where json is a string and options is an object that may contain the following:
|
||||
<MediaQuery smallerThan="sm" styles={{ display: "none" }}>
|
||||
<Prism w={500} language="json">
|
||||
{
|
||||
'{\n theme: "light" | "dark",\n direction: "TOP" | "RIGHT" | "DOWN" | "LEFT"\n}'
|
||||
}
|
||||
</Prism>
|
||||
</MediaQuery>
|
||||
<CodeHighlight
|
||||
w={500}
|
||||
language="json"
|
||||
code={
|
||||
'{\n theme: "light" | "dark",\n direction: "TOP" | "RIGHT" | "DOWN" | "LEFT"\n}'
|
||||
}
|
||||
withCopyButton={false}
|
||||
/>
|
||||
</Text>
|
||||
|
||||
<StyledFrame
|
||||
|
@ -33,7 +33,7 @@ function ResetPassword() {
|
||||
|
||||
return (
|
||||
<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
|
||||
</Text>
|
||||
|
||||
@ -46,6 +46,7 @@ function ResetPassword() {
|
||||
label="New Password"
|
||||
radius="sm"
|
||||
placeholder="∗∗∗∗∗∗∗∗∗∗∗"
|
||||
style={{ color: "black" }}
|
||||
/>
|
||||
<PasswordInput
|
||||
value={password2}
|
||||
@ -54,10 +55,11 @@ function ResetPassword() {
|
||||
label="Validate Password"
|
||||
radius="sm"
|
||||
placeholder="∗∗∗∗∗∗∗∗∗∗∗"
|
||||
style={{ color: "black" }}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
<Group position="apart" mt="xl">
|
||||
<Group justify="apart" mt="xl">
|
||||
<Button color="dark" type="submit" radius="sm" loading={loading} fullWidth>
|
||||
Reset Password
|
||||
</Button>
|
||||
@ -101,7 +103,7 @@ const ForgotPassword = () => {
|
||||
<ResetPassword />
|
||||
) : (
|
||||
<Paper mx="auto" mt={70} maw={400} p="lg" withBorder>
|
||||
<Text size="lg" weight={500}>
|
||||
<Text size="lg" w={500} c="dark">
|
||||
Reset Password
|
||||
</Text>
|
||||
<Paper pt="lg">
|
||||
@ -118,16 +120,17 @@ const ForgotPassword = () => {
|
||||
label="Email"
|
||||
placeholder="hello@herowand.com"
|
||||
radius="sm"
|
||||
style={{ color: "black" }}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
<Group position="apart" mt="xl">
|
||||
<Group justify="apart" mt="xl">
|
||||
<Button color="dark" type="submit" radius="sm" loading={loading} fullWidth>
|
||||
Reset Password
|
||||
</Button>
|
||||
</Group>
|
||||
<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't have an account? Sign Up
|
||||
</Anchor>
|
||||
</Stack>
|
||||
|
@ -1,53 +1,71 @@
|
||||
import React from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import Head from "next/head";
|
||||
import Link from "next/link";
|
||||
import Script from "next/script";
|
||||
import styled, { ThemeProvider } from "styled-components";
|
||||
import {
|
||||
Anchor,
|
||||
Button,
|
||||
Center,
|
||||
Container,
|
||||
Flex,
|
||||
Group,
|
||||
MediaQuery,
|
||||
Stack,
|
||||
Text,
|
||||
Title,
|
||||
Tooltip,
|
||||
rem,
|
||||
} from "@mantine/core";
|
||||
import { Button, Group, Stack, Title, Text } from "@mantine/core";
|
||||
import styled from "styled-components";
|
||||
import { FaChevronRight } from "react-icons/fa";
|
||||
import { SiVisualstudiocode } from "react-icons/si";
|
||||
import { Typewriter } from "react-simple-typewriter";
|
||||
import { HovercardAds } from "src/components/HovercardAds";
|
||||
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));
|
||||
import Layout from "src/layout/Layout";
|
||||
|
||||
const StyledHeroSection = styled.div`
|
||||
--bg-color: ${({ theme }) => theme.GRID_BG_COLOR};
|
||||
--line-color-1: ${({ theme }) => theme.GRID_COLOR_PRIMARY};
|
||||
--line-color-2: ${({ theme }) => theme.GRID_COLOR_SECONDARY};
|
||||
|
||||
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);
|
||||
position: relative;
|
||||
background-size: 100% 100%;
|
||||
margin-bottom: -48px;
|
||||
background-position:
|
||||
-1.5px -1.5px,
|
||||
-1.5px -1.5px,
|
||||
-1px -1px,
|
||||
-1px -1px;
|
||||
background-size:
|
||||
100px 100px,
|
||||
100px 100px,
|
||||
20px 20px,
|
||||
20px 20px;
|
||||
0px 0px,
|
||||
0px 0px,
|
||||
0px 0px,
|
||||
0px 0px,
|
||||
0px 0px;
|
||||
background-image: radial-gradient(49% 81% at 45% 47%, #26001fff 1%, #60006a00 100%),
|
||||
radial-gradient(113% 91% at 17% -2%, #0f000cff 1%, #ff000000 99%),
|
||||
radial-gradient(142% 91% at 83% 7%, #0f000cff 1%, #ff000000 99%),
|
||||
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) {
|
||||
flex-direction: column;
|
||||
@ -63,231 +81,94 @@ const StyledHeroSectionBody = styled.div`
|
||||
overflow: hidden;
|
||||
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) {
|
||||
flex-direction: column;
|
||||
}
|
||||
`;
|
||||
|
||||
const Left = styled.div`
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
`;
|
||||
const StyledHighlightedText = styled(Text)`
|
||||
font-size: 40px;
|
||||
font-weight: 800;
|
||||
display: inline;
|
||||
|
||||
const Right = styled.div`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
transform: translate(20em, 15em) rotate(3deg);
|
||||
width: 80%;
|
||||
filter: blur(1px);
|
||||
user-select: none;
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledHighlightedText = styled.span`
|
||||
text-decoration: underline;
|
||||
text-decoration-style: wavy;
|
||||
text-decoration-color: #eab308;
|
||||
background-color: gray;
|
||||
background-image: linear-gradient(45deg, gray, slategray);
|
||||
background-size: 100%;
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
-moz-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
-moz-text-fill-color: transparent;
|
||||
`;
|
||||
|
||||
const StyledHeroText = styled.p`
|
||||
font-size: 18px;
|
||||
color: #5e656b;
|
||||
color: #a9aaaa;
|
||||
font-weight: 600;
|
||||
max-width: 600px;
|
||||
text-align: center;
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
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 = () => (
|
||||
<StyledHeroSection id="hero-section">
|
||||
<Navbar />
|
||||
<StyledHeroSectionBody>
|
||||
<Left>
|
||||
<Stack w="100%" mx="auto">
|
||||
<MediaQuery query="(max-width: 40em)" styles={{ fontSize: rem(30) }}>
|
||||
<Title order={1} fz={40} c="gray" fw={800}>
|
||||
Visualize{" "}
|
||||
<StyledHighlightedText>
|
||||
<Typewriter
|
||||
words={["JSON", "YAML", "XML", "TOML", "CSV"]}
|
||||
typeSpeed={100}
|
||||
deleteSpeed={60}
|
||||
delaySpeed={2000}
|
||||
loop
|
||||
/>
|
||||
</StyledHighlightedText>
|
||||
<br />
|
||||
instantly into
|
||||
<Text
|
||||
variant="gradient"
|
||||
gradient={{ from: "purple", to: "orange", deg: 45 }}
|
||||
display="inline"
|
||||
>
|
||||
{" "}
|
||||
graphs
|
||||
</Text>
|
||||
</Title>
|
||||
</MediaQuery>
|
||||
<Stack w="100%" mx="auto" align="center">
|
||||
<Title c="#d0c9c9" order={1} fz={40} fw={800} style={{ textAlign: "center" }}>
|
||||
Understand your{" "}
|
||||
<StyledHighlightedText>
|
||||
<Typewriter
|
||||
words={["JSON", "YAML", "XML", "TOML", "CSV"]}
|
||||
typeSpeed={100}
|
||||
deleteSpeed={60}
|
||||
delaySpeed={2000}
|
||||
loop
|
||||
/>
|
||||
</StyledHighlightedText>
|
||||
<br />
|
||||
better by visualizing
|
||||
</Title>
|
||||
|
||||
<StyledHeroText>
|
||||
Visualize, analyze, and manipulate data with ease, a versatile and powerful tool for
|
||||
data representation and exploration.
|
||||
</StyledHeroText>
|
||||
<Group spacing="xl">
|
||||
<Button
|
||||
component={Link}
|
||||
href="/editor"
|
||||
prefetch={false}
|
||||
fw="bold"
|
||||
rightIcon={<FaChevronRight />}
|
||||
size="lg"
|
||||
>
|
||||
GO TO EDITOR
|
||||
</Button>
|
||||
<Tooltip
|
||||
maw={400}
|
||||
label="VS Code extension only contains JSON visualization without additional features."
|
||||
withArrow
|
||||
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>
|
||||
<StyledHeroText>
|
||||
Visualize, analyze, and manipulate data with ease, a versatile and powerful tool for data
|
||||
representation and exploration.
|
||||
</StyledHeroText>
|
||||
<Group gap="xl">
|
||||
<Button
|
||||
color="orange"
|
||||
variant="light"
|
||||
component={Link}
|
||||
href="/editor"
|
||||
fw="bold"
|
||||
rightSection={<FaChevronRight />}
|
||||
size="xl"
|
||||
style={{ border: "2px solid orange" }}
|
||||
>
|
||||
GO TO EDITOR
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</StyledHeroSectionBody>
|
||||
</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'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 = () => {
|
||||
const [ads, setAds] = React.useState(false);
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<Layout>
|
||||
<Head>
|
||||
<title>JSON Crack | Visualize Instantly Into Graphs</title>
|
||||
</Head>
|
||||
<HeroSection />
|
||||
<StatsBanner />
|
||||
<FeaturesCards />
|
||||
<HeroBottom />
|
||||
<Footer />
|
||||
{ads && <HovercardAds />}
|
||||
<Script src="https://m.servedby-buysellads.com/monetization.js" onLoad={() => setAds(true)} />
|
||||
</ThemeProvider>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -9,39 +9,49 @@ const Privacy = () => {
|
||||
<Head>
|
||||
<title>Privacy Policy - JSON Crack</title>
|
||||
</Head>
|
||||
<Container my={50} size="sm">
|
||||
<Container my={50} size="sm" pb="lg">
|
||||
<Paper bg="transparent">
|
||||
<Title>Privacy Policy</Title>
|
||||
<Title c="dark">Privacy Policy</Title>
|
||||
<Stack my="lg">
|
||||
<Text>
|
||||
<Text c="dark">
|
||||
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”).
|
||||
</Text>
|
||||
<Title order={3}>To Whom Does This Policy Apply</Title>
|
||||
<Text>
|
||||
<Title order={3} c="dark">
|
||||
To Whom Does This Policy Apply
|
||||
</Title>
|
||||
<Text c="dark">
|
||||
This Privacy Policy applies to customers and site visitors. Each customer is
|
||||
responsible for posting its own terms, conditions, and privacy policies, and ensuring
|
||||
compliance with all applicable laws and regulations.
|
||||
</Text>
|
||||
<Title order={3}>Changes To This Privacy Policy</Title>
|
||||
<Text>
|
||||
<Title order={3} c="dark">
|
||||
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
|
||||
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.
|
||||
</Text>
|
||||
<Title order={3}>What Information Do We Collect</Title>
|
||||
<Text>
|
||||
<Title order={3} c="dark">
|
||||
What Information Do We Collect
|
||||
</Title>
|
||||
<Text c="dark">
|
||||
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.
|
||||
</Text>
|
||||
<Title order={3}>What We Use Your Information For</Title>
|
||||
<Text>
|
||||
<Title order={3} c="dark">
|
||||
What We Use Your Information For
|
||||
</Title>
|
||||
<Text c="dark">
|
||||
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
|
||||
personal information to third parties.
|
||||
</Text>
|
||||
<Title order={3}>How To Contact Us</Title>
|
||||
<Text>
|
||||
<Title order={3} c="dark">
|
||||
How To Contact Us
|
||||
</Title>
|
||||
<Text c="dark">
|
||||
For privacy-related questions, please contact us at{" "}
|
||||
<Anchor href="mailto:contact@jsoncrack.com">contact@jsoncrack.com</Anchor>.
|
||||
</Text>
|
||||
|
@ -9,25 +9,29 @@ const SubscriptionRefund = () => {
|
||||
<Head>
|
||||
<title>Subscription & Refund - JSON Crack</title>
|
||||
</Head>
|
||||
<Container my={50} size="sm">
|
||||
<Container my={50} size="sm" pb="lg">
|
||||
<Paper bg="transparent">
|
||||
<Title>Subscription & Refund</Title>
|
||||
<Title c="dark">Subscription & Refund</Title>
|
||||
<Stack my="lg">
|
||||
<Text>
|
||||
<Text c="dark">
|
||||
This document delineates the Subscription Cancellation and Refund Policy for users of
|
||||
jsoncrack.com (the “Site”). It provides guidance on the cancellation process. For
|
||||
inquiries or assistance related to cancellations, users are encouraged to contact the
|
||||
customer support team at{" "}
|
||||
<Anchor href="mailto:contact@jsoncrack.com">contact@jsoncrack.com</Anchor>.
|
||||
</Text>
|
||||
<Title order={3}>Cancellation Policy</Title>
|
||||
<Text>
|
||||
<Title order={3} c="dark">
|
||||
Cancellation Policy
|
||||
</Title>
|
||||
<Text c="dark">
|
||||
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
|
||||
not be billed for any subsequent periods.
|
||||
</Text>
|
||||
<Title order={4}>How to Cancel:</Title>
|
||||
<Text>
|
||||
<Title order={4} c="dark">
|
||||
How to Cancel:
|
||||
</Title>
|
||||
<Text c="dark">
|
||||
To cancel your subscription, follow these steps:
|
||||
<List type="ordered" my="lg">
|
||||
<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
|
||||
the end of the current billing period.
|
||||
</Text>
|
||||
<Title order={3}>Refund Policy</Title>
|
||||
<Text>
|
||||
<Title order={3} c="dark">
|
||||
Refund Policy
|
||||
</Title>
|
||||
<Text c="dark">
|
||||
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
|
||||
during the purchase. For refund inquiries or assistance, please contact our customer
|
||||
support team at{" "}
|
||||
<Anchor href="mailto:contact@jsoncrack.com">contact@jsoncrack.com</Anchor>.
|
||||
</Text>
|
||||
<Title order={3}>Changes to this Policy</Title>
|
||||
<Text>
|
||||
<Title order={3} c="dark">
|
||||
Changes to this Policy
|
||||
</Title>
|
||||
<Text c="dark">
|
||||
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
|
||||
website. It is your responsibility to review this policy periodically for changes. By
|
||||
|
@ -9,11 +9,11 @@ const Terms = () => {
|
||||
<Head>
|
||||
<title>Terms of Service - JSON Crack</title>
|
||||
</Head>
|
||||
<Container my={50} size="sm">
|
||||
<Container my={50} size="sm" pb="lg">
|
||||
<Paper bg="transparent">
|
||||
<Title>Terms of Service</Title>
|
||||
<Title c="dark">Terms of Service</Title>
|
||||
<Stack my="lg">
|
||||
<Text>
|
||||
<Text c="dark">
|
||||
Subject to these Terms of Service (this 'Agreement'), jsoncrack.com
|
||||
('JSON Crack', 'we', 'us' and/or 'our')
|
||||
provides access to JSON Crack’s application as a service (collectively, the
|
||||
@ -21,7 +21,7 @@ const Terms = () => {
|
||||
have read, understand, and agree to be bound by this Agreement.
|
||||
</Text>
|
||||
|
||||
<Text>
|
||||
<Text c="dark">
|
||||
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
|
||||
Agreement, in which case the term 'you' shall refer to such entity. If you
|
||||
@ -29,30 +29,38 @@ const Terms = () => {
|
||||
accept this Agreement and may not use the Services.
|
||||
</Text>
|
||||
|
||||
<Title order={3}>1. Acceptance of Terms</Title>
|
||||
<Text>
|
||||
<Title order={3} c="dark">
|
||||
1. Acceptance of Terms
|
||||
</Title>
|
||||
<Text c="dark">
|
||||
By signing up and using the services provided by JSON Crack (referred to as the
|
||||
'Service'), you are agreeing to be bound by the following terms and
|
||||
conditions ('Terms of Service'). The Service is owned and operated by JSON
|
||||
Crack ('Us', 'We', or 'Our').
|
||||
</Text>
|
||||
<Title order={3}>2. Description of Service</Title>
|
||||
<Text>
|
||||
<Title order={3} c="dark">
|
||||
2. Description of Service
|
||||
</Title>
|
||||
<Text c="dark">
|
||||
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
|
||||
graphs for visualization purposes (the “Product”). The Product is accessible at
|
||||
jsoncrack.com and other domains and subdomains controlled by Us (collectively,
|
||||
'the Website').
|
||||
</Text>
|
||||
<Title order={3}>3. Fair Use</Title>
|
||||
<Text>
|
||||
<Title order={3} c="dark">
|
||||
3. Fair Use
|
||||
</Title>
|
||||
<Text c="dark">
|
||||
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,
|
||||
including but not limited to, purposefully advertising of third parties, spam content,
|
||||
or other inappropriate or illegal content.
|
||||
</Text>
|
||||
<Title order={3}>4. Intellectual Property Rights</Title>
|
||||
<Text>
|
||||
<Title order={3} c="dark">
|
||||
4. Intellectual Property Rights
|
||||
</Title>
|
||||
<Text c="dark">
|
||||
You acknowledge and agree that the Service and its entire contents, features, and
|
||||
functionality, including but not limited to all information, software, code, text,
|
||||
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
|
||||
secret, and other intellectual property or proprietary rights laws.
|
||||
</Text>
|
||||
<Title order={3}>5. Data Storage and Privacy</Title>
|
||||
<Text>
|
||||
<Title order={3} c="dark">
|
||||
5. Data Storage and Privacy
|
||||
</Title>
|
||||
<Text c="dark">
|
||||
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
|
||||
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
|
||||
as hard deletion).
|
||||
</Text>
|
||||
<Title order={3}>6. Changes to these Terms</Title>
|
||||
<Text>
|
||||
<Title order={3} c="dark">
|
||||
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
|
||||
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
|
||||
Website following the posting of revised Terms of Service means that you accept and
|
||||
agree to the changes.
|
||||
</Text>
|
||||
<Title order={3}>7. Contact Information</Title>
|
||||
<Text>
|
||||
<Title order={3} c="dark">
|
||||
7. Contact Information
|
||||
</Title>
|
||||
<Text c="dark">
|
||||
Questions or comments about the Website or these Terms of Service may be directed to
|
||||
our support team at{" "}
|
||||
<Anchor href="mailto:contact@jsoncrack.com">contact@jsoncrack.com</Anchor>.
|
||||
</Text>
|
||||
<Title order={3}>8. Disclaimer of Warranties</Title>
|
||||
<Text>
|
||||
<Title order={3} c="dark">
|
||||
8. Disclaimer of Warranties
|
||||
</Title>
|
||||
<Text c="dark">
|
||||
THE SERVICE AND ITS CONTENT ARE PROVIDED ON AN 'AS IS' AND 'AS
|
||||
AVAILABLE' BASIS WITHOUT ANY WARRANTIES OF ANY KIND. WE DISCLAIM ALL WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE WARRANTY OF TITLE, MERCHANTABILITY,
|
||||
NON-INFRINGEMENT OF THIRD PARTIES’ RIGHTS, AND FITNESS FOR PARTICULAR PURPOSE.
|
||||
</Text>
|
||||
<Text>
|
||||
<Text c="dark">
|
||||
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
|
||||
THEORY, ARISING OUT OF OR IN CONNECTION WITH YOUR USE, OR INABILITY TO USE, THE
|
||||
|
@ -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}>
|
||||
“We would like to extend our sincerest gratitude to all of our sponsors for their
|
||||
invaluable support and contribution towards JSON Crack.”
|
||||
</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() || [],
|
||||
},
|
||||
};
|
||||
}
|
@ -13,13 +13,13 @@ const Pricing = () => {
|
||||
<title>Pricing - JSON Crack</title>
|
||||
</Head>
|
||||
<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">
|
||||
<Stack spacing="0">
|
||||
<Text fz="xl" fw="bold">
|
||||
<Stack gap="0">
|
||||
<Text fz="xl" fw="bold" c="dark">
|
||||
Free
|
||||
</Text>
|
||||
<Text fz="xs" fw="bold" color="gray.6">
|
||||
<Text fz="xs" fw="bold" c="gray.6">
|
||||
Free - forever.
|
||||
</Text>
|
||||
</Stack>
|
||||
@ -37,17 +37,20 @@ const Pricing = () => {
|
||||
</ThemeIcon>
|
||||
}
|
||||
>
|
||||
<List.Item>Limited capability</List.Item>
|
||||
<List.Item>Save & share up to 15 files</List.Item>
|
||||
<List.Item>Visualize all data formats</List.Item>
|
||||
<List.Item
|
||||
icon={
|
||||
<ThemeIcon color="gray.5" size={20} radius="xl">
|
||||
<BsX size="1rem" />
|
||||
</ThemeIcon>
|
||||
}
|
||||
>
|
||||
<Text color="gray.6">JSON Schema</Text>
|
||||
<List.Item>
|
||||
<Text c="dark" fz="sm">
|
||||
Maximum capability
|
||||
</Text>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<Text c="dark" fz="sm">
|
||||
Save & share up to 15 files
|
||||
</Text>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<Text c="dark" fz="sm">
|
||||
Visualize all data formats
|
||||
</Text>
|
||||
</List.Item>
|
||||
<List.Item
|
||||
icon={
|
||||
@ -56,7 +59,20 @@ const Pricing = () => {
|
||||
</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>
|
||||
<Button size="md" radius="md" color="orange" variant="outline">
|
||||
@ -65,9 +81,9 @@ const Pricing = () => {
|
||||
</Flex>
|
||||
</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">
|
||||
<Stack spacing="0">
|
||||
<Stack gap="0">
|
||||
<Text c="white" fz="xl" fw="bold">
|
||||
Premium
|
||||
</Text>
|
||||
@ -76,11 +92,11 @@ const Pricing = () => {
|
||||
</Badge>
|
||||
</Stack>
|
||||
<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">
|
||||
$7
|
||||
</Text>
|
||||
<Text fz="xs" color="gray.4" fw="bold">
|
||||
<Text fz="xs" c="gray.4" fw="bold">
|
||||
Per month
|
||||
</Text>
|
||||
</Stack>
|
||||
@ -100,19 +116,29 @@ const Pricing = () => {
|
||||
}
|
||||
>
|
||||
<List.Item>
|
||||
<Text c="white">Maximum capability</Text>
|
||||
<Text c="white" fz="sm">
|
||||
Maximum capability
|
||||
</Text>
|
||||
</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>
|
||||
<Text c="white">Visualize all data formats</Text>
|
||||
<Text c="white" fz="sm">
|
||||
Visualize all data formats
|
||||
</Text>
|
||||
</List.Item>
|
||||
<List.Item>
|
||||
<Text c="white">JSON Schema</Text>
|
||||
<Text c="white" fz="sm">
|
||||
JSON Schema
|
||||
</Text>
|
||||
</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>
|
||||
<Button
|
||||
@ -128,22 +154,22 @@ const Pricing = () => {
|
||||
</Flex>
|
||||
</Paper>
|
||||
|
||||
<Paper p="xl" radius="lg" shadow="lg" withBorder w={325}>
|
||||
<Paper p="xl" radius="lg" withBorder w={325}>
|
||||
<Flex justify="space-between">
|
||||
<Stack spacing="0">
|
||||
<Text fz="xl" fw="bold">
|
||||
<Stack gap="0">
|
||||
<Text fz="xl" fw="bold" c="dark">
|
||||
Enterprise
|
||||
</Text>
|
||||
<Text fz="xs" fw="bold" color="gray.6">
|
||||
<Text fz="xs" fw="bold" c="gray.6">
|
||||
For Teams & Organizations
|
||||
</Text>
|
||||
</Stack>
|
||||
<Paper py={5} px="sm" bg="gray.0">
|
||||
<Stack spacing="0" align="center" justify="center">
|
||||
<Text fz="lg" fw="bolder">
|
||||
<Paper py={5} px="sm" bg="gray.0" radius="xs">
|
||||
<Stack gap="0" align="center" justify="center">
|
||||
<Text fz="lg" fw="bolder" c="dark">
|
||||
$120
|
||||
</Text>
|
||||
<Text fz="xs" color="gray.6" fw="bold">
|
||||
<Text fz="xs" c="gray.6" fw="bold">
|
||||
Per month
|
||||
</Text>
|
||||
</Stack>
|
||||
@ -161,6 +187,7 @@ const Pricing = () => {
|
||||
<BsCheck size="1rem" />
|
||||
</ThemeIcon>
|
||||
}
|
||||
c="dark"
|
||||
>
|
||||
<List.Item>Everything from previous plans</List.Item>
|
||||
<List.Item
|
||||
@ -169,6 +196,7 @@ const Pricing = () => {
|
||||
<BsCheck size="1rem" />
|
||||
</ThemeIcon>
|
||||
}
|
||||
c="dark"
|
||||
>
|
||||
Unlimited premium accounts for work email
|
||||
</List.Item>
|
||||
@ -178,6 +206,7 @@ const Pricing = () => {
|
||||
<BsCheck size="1rem" />
|
||||
</ThemeIcon>
|
||||
}
|
||||
c="dark"
|
||||
>
|
||||
Shared cloud in app
|
||||
</List.Item>
|
||||
@ -195,7 +224,7 @@ const Pricing = () => {
|
||||
</Flex>
|
||||
</Paper>
|
||||
</Flex>
|
||||
<Text align="center" size="sm" color="dimmed">
|
||||
<Text size="sm" c="dimmed" style={{ textAlign: "center" }}>
|
||||
<AiOutlineInfoCircle style={{ marginRight: "4px" }} />
|
||||
Payment email must be matching with the account registered to the JSON Crack.
|
||||
</Text>
|
||||
|
@ -65,6 +65,7 @@ export function AuthenticationForm(props: PaperProps) {
|
||||
value={userData.email}
|
||||
onChange={event => setUserData(d => ({ ...d, email: event.target.value }))}
|
||||
radius="sm"
|
||||
style={{ color: "black" }}
|
||||
/>
|
||||
|
||||
<PasswordInput
|
||||
@ -75,20 +76,15 @@ export function AuthenticationForm(props: PaperProps) {
|
||||
value={userData.password}
|
||||
onChange={event => setUserData(d => ({ ...d, password: event.target.value }))}
|
||||
radius="sm"
|
||||
style={{ color: "black" }}
|
||||
/>
|
||||
|
||||
<Button color="dark" type="submit" radius="sm" loading={loading}>
|
||||
Sign in
|
||||
</Button>
|
||||
|
||||
<Stack spacing="sm" mx="auto" align="center">
|
||||
<Anchor
|
||||
component={Link}
|
||||
prefetch={false}
|
||||
href="/forgot-password"
|
||||
color="dark"
|
||||
size="xs"
|
||||
>
|
||||
<Stack gap="sm" mx="auto" align="center">
|
||||
<Anchor component={Link} prefetch={false} href="/forgot-password" c="dark" size="xs">
|
||||
Forgot your password?
|
||||
</Anchor>
|
||||
</Stack>
|
||||
@ -100,7 +96,7 @@ export function AuthenticationForm(props: PaperProps) {
|
||||
<Stack mb="md" mt="md">
|
||||
<Button
|
||||
radius="sm"
|
||||
leftIcon={<AiOutlineGoogle size="20" />}
|
||||
leftSection={<AiOutlineGoogle size="20" />}
|
||||
onClick={() => handleLoginClick("google")}
|
||||
color="red"
|
||||
variant="outline"
|
||||
@ -109,7 +105,7 @@ export function AuthenticationForm(props: PaperProps) {
|
||||
</Button>
|
||||
<Button
|
||||
radius="sm"
|
||||
leftIcon={<AiOutlineGithub size="20" />}
|
||||
leftSection={<AiOutlineGithub size="20" />}
|
||||
onClick={() => handleLoginClick("github")}
|
||||
color="dark"
|
||||
variant="outline"
|
||||
@ -136,11 +132,11 @@ const SignIn = () => {
|
||||
<Head>
|
||||
<title>Sign In - JSON Crack</title>
|
||||
</Head>
|
||||
<Paper mt={50} shadow="xs" mx="auto" maw={400} p="lg" withBorder>
|
||||
<Paper mt={50} mx="auto" maw={400} p="lg" withBorder>
|
||||
<AuthenticationForm />
|
||||
</Paper>
|
||||
<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't have an account?
|
||||
</Anchor>
|
||||
</Center>
|
||||
|
@ -60,13 +60,13 @@ const SignUp = () => {
|
||||
<title>JSON Crack | Sign Up</title>
|
||||
</Head>
|
||||
{done ? (
|
||||
<Paper shadow="xs" mx="auto" maw={400} mt={50} p="lg" withBorder>
|
||||
<Text align="center" mt="lg">
|
||||
<Paper mx="auto" maw={400} mt={50} p="lg" withBorder>
|
||||
<Text mt="lg" style={{ textAlign: "center" }}>
|
||||
Registration successul!
|
||||
<br />
|
||||
Please check your inbox for email confirmation.
|
||||
</Text>
|
||||
<Anchor component={Link} href="/sign-in" underline={false}>
|
||||
<Anchor component={Link} href="/sign-in">
|
||||
<Button color="dark" radius="sm" mt="lg" fullWidth>
|
||||
Back to login
|
||||
</Button>
|
||||
@ -74,7 +74,7 @@ const SignUp = () => {
|
||||
</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}>
|
||||
<Stack>
|
||||
<TextInput
|
||||
@ -83,6 +83,7 @@ const SignUp = () => {
|
||||
label="Name"
|
||||
placeholder="John Doe"
|
||||
radius="sm"
|
||||
style={{ color: "black" }}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
@ -92,6 +93,7 @@ const SignUp = () => {
|
||||
label="Email"
|
||||
placeholder="hello@jsoncrack.com"
|
||||
radius="sm"
|
||||
style={{ color: "black" }}
|
||||
/>
|
||||
|
||||
<PasswordInput
|
||||
@ -101,6 +103,7 @@ const SignUp = () => {
|
||||
label="Password"
|
||||
placeholder="∗∗∗∗∗∗∗∗∗∗"
|
||||
radius="sm"
|
||||
style={{ color: "black" }}
|
||||
/>
|
||||
|
||||
<Button color="dark" type="submit" loading={loading}>
|
||||
@ -113,7 +116,7 @@ const SignUp = () => {
|
||||
<Button
|
||||
radius="sm"
|
||||
fullWidth
|
||||
leftIcon={<AiOutlineGoogle size="20" />}
|
||||
leftSection={<AiOutlineGoogle size="20" />}
|
||||
onClick={() => handleLoginClick("google")}
|
||||
color="red"
|
||||
variant="outline"
|
||||
@ -122,7 +125,7 @@ const SignUp = () => {
|
||||
</Button>
|
||||
<Button
|
||||
radius="sm"
|
||||
leftIcon={<AiOutlineGithub size="20" />}
|
||||
leftSection={<AiOutlineGithub size="20" />}
|
||||
onClick={() => handleLoginClick("github")}
|
||||
color="dark"
|
||||
variant="outline"
|
||||
@ -137,15 +140,21 @@ const SignUp = () => {
|
||||
|
||||
<Text fz="xs" c="gray">
|
||||
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
|
||||
</Anchor>{" "}
|
||||
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
|
||||
</Anchor>
|
||||
. 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.
|
||||
</Anchor>
|
||||
</Text>
|
||||
@ -154,7 +163,7 @@ const SignUp = () => {
|
||||
</Paper>
|
||||
|
||||
<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?
|
||||
</Anchor>
|
||||
</Center>
|
||||
|
@ -2,11 +2,11 @@ import React from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import { MantineProvider, useMantineColorScheme } from "@mantine/core";
|
||||
import { ThemeProvider } from "styled-components";
|
||||
import toast from "react-hot-toast";
|
||||
import { darkTheme, lightTheme } from "src/constants/theme";
|
||||
import { Toolbar } from "src/containers/Toolbar";
|
||||
import { EditorWrapper } from "src/layout/EditorWrapper";
|
||||
import useFile from "src/store/useFile";
|
||||
import useGraph from "src/store/useGraph";
|
||||
|
||||
@ -23,11 +23,12 @@ const Graph = dynamic(() => import("src/containers/Views/GraphView").then(c => c
|
||||
|
||||
const WidgetPage = () => {
|
||||
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 setContents = useFile(state => state.setContents);
|
||||
const setDirection = useGraph(state => state.setDirection);
|
||||
const clearGraph = useGraph(state => state.clearGraph);
|
||||
const { setColorScheme } = useMantineColorScheme();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isReady) {
|
||||
@ -43,6 +44,7 @@ const WidgetPage = () => {
|
||||
if (!event.data?.json) return;
|
||||
if (event.data?.options?.theme === "light" || event.data?.options?.theme === "dark") {
|
||||
setTheme(event.data.options.theme);
|
||||
setColorScheme(event.data.options.theme);
|
||||
}
|
||||
|
||||
setContents({ contents: event.data.json, hasChanges: false });
|
||||
@ -58,15 +60,15 @@ const WidgetPage = () => {
|
||||
}, [setContents, setDirection, theme]);
|
||||
|
||||
return (
|
||||
<EditorWrapper>
|
||||
<Head>
|
||||
<meta name="robots" content="noindex,nofollow" />
|
||||
</Head>
|
||||
<MantineProvider forceColorScheme={theme}>
|
||||
<ThemeProvider theme={theme === "dark" ? darkTheme : lightTheme}>
|
||||
<Head>
|
||||
<meta name="robots" content="noindex,nofollow" />
|
||||
</Head>
|
||||
<Toolbar isWidget />
|
||||
<Graph isWidget />
|
||||
</ThemeProvider>
|
||||
</EditorWrapper>
|
||||
</MantineProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user