feat: upgrade mantine v7 & ui changes

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

View File

@ -26,9 +26,9 @@ jobs:
- name: Detect package manager
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

View File

@ -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
View File

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

View File

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

View File

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

View File

@ -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

View File

@ -58,11 +58,11 @@ If you like the project, you can become a sponsor at [GitHub Sponsors](https://g
After cloning the repository, run the following commands:
```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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -19,10 +19,10 @@ export const SearchInput: React.FC = () => {
onFocus={() => ReactGA.event({ action: "focus_node_search", category: "User" })}
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>

View File

@ -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;

View File

@ -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",

View File

@ -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 && (

View File

@ -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>

View File

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

View File

@ -27,7 +27,7 @@ export const AccountModal: React.FC<ModalProps> = ({ opened, onClose }) => {
return (
<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!

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

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

View File

@ -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>
);

View File

@ -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>

View File

@ -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>

View File

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

View File

@ -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

View File

@ -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>

View File

@ -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>
</>

View File

@ -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">

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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" },

View File

@ -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>

View File

@ -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 && (
<>

View File

@ -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;

View File

@ -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>

View File

@ -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>

View File

@ -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"

View File

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

View File

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

View File

@ -6,18 +6,11 @@ import { monaSans } from "src/constants/fonts";
const StyledTitle = styled.div<{ fontSize: string }>`
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>
);
};

View File

@ -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>
);
};

View File

@ -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>

View File

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

View File

@ -43,8 +43,8 @@ const NotFound: React.FC = () => {
<StyledImageWrapper>
<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&apos;re lost, let&apos;s head back to the home!</Text>
<Title c="dark">WIZARDS BEHIND CURTAINS?</Title>
<Text c="dark">Looks like you&apos;re lost, let&apos;s head back to the home!</Text>
<Button mt="lg" size="lg" type="button" onClick={() => router.push("/")}>
Go Home
</Button>

View File

@ -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>
);
}

View File

@ -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,

View File

@ -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 &quot;json&quot; and &quot;options&quot; 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

View File

@ -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&apos;t have an account? Sign Up
</Anchor>
</Stack>

View File

@ -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&apos;s not all yet!
<br />
Explore the full potential of your data now......
</Title>
<Center>
<Button
component={Link}
href="/editor"
prefetch={false}
color="violet"
fw="bold"
rightIcon={<FaChevronRight />}
size="lg"
>
GO TO EDITOR
</Button>
</Center>
</Stack>
</Container>
);
export const HomePage = () => {
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>
);
};

View File

@ -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>

View File

@ -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

View File

@ -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 &apos;Agreement&apos;), jsoncrack.com
(&apos;JSON Crack&apos;, &apos;we&apos;, &apos;us&apos; and/or &apos;our&apos;)
provides access to JSON Cracks application as a service (collectively, the
@ -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 &apos;you&apos; shall refer to such entity. If you
@ -29,30 +29,38 @@ const Terms = () => {
accept this Agreement and may not use the Services.
</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
&apos;Service&apos;), you are agreeing to be bound by the following terms and
conditions (&apos;Terms of Service&apos;). The Service is owned and operated by JSON
Crack (&apos;Us&apos;, &apos;We&apos;, or &apos;Our&apos;).
</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,
&apos;the Website&apos;).
</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 &apos;AS IS&apos; AND &apos;AS
AVAILABLE&apos; 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

View File

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

View File

@ -13,13 +13,13 @@ const Pricing = () => {
<title>Pricing - JSON Crack</title>
</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>

View File

@ -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&apos;t have an account?
</Anchor>
</Center>

View File

@ -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>

View File

@ -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>
);
};

5162
yarn.lock

File diff suppressed because it is too large Load Diff