Initial commit with something working

pull/2/head
Tim Pechersky 2021-07-19 19:50:35 +08:00
rodzic 0e8fc39824
commit 66f7510ff1
49 zmienionych plików z 1959 dodań i 1472 usunięć

2
.gitignore vendored 100644
Wyświetl plik

@ -0,0 +1,2 @@
.vscode/
.DS_store

Wyświetl plik

@ -17,14 +17,18 @@
"axios": "^0.21.1",
"framer-motion": "^4.1.17",
"mixpanel-browser": "^2.41.0",
"moment": "^2.29.1",
"next": "11.0.1",
"react": "^17.0.2",
"react-calendly": "^2.2.1",
"react-copy-to-clipboard": "^5.0.2",
"react-dom": "^17.0.2",
"react-hook-form": "^6.9.2",
"react-icons": "^4.2.0",
"react-pro-sidebar": "^0.6.0",
"react-query": "^3.18.1",
"react-showdown": "^2.3.0",
"react-split-pane": "^0.1.92",
"showdown": "^1.9.1",
"uuid": "^8.3.2"
},
@ -35,6 +39,7 @@
"@babel/plugin-proposal-class-properties": "^7.13.0",
"@testing-library/dom": "^7.31.2",
"@types/react": "17.0.13",
"axios-mock-adapter": "^1.19.0",
"eslint": "7.29.0",
"eslint-config-next": "11.0.1",
"eslint-config-prettier": "^8.3.0",

Wyświetl plik

@ -0,0 +1,152 @@
import React, { useState, useEffect } from "react";
import { useForm } from "react-hook-form";
import { useChangePassword, useRouter } from "../../src/core/hooks";
import {
Box,
FormControl,
FormErrorMessage,
InputGroup,
Stack,
Center,
Button,
Input,
InputRightElement,
ScaleFade,
Heading,
} from "@chakra-ui/react";
import { Icon } from "../../src/Theme";
import { getLayout } from "../../src/layouts/AccountLayout";
const Security = () => {
const headingStyle = {
as: "h2",
pt: 2,
mb: 4,
borderBottom: "solid",
borderColor: "primary.50",
borderBottomWidth: "2px",
};
const router = useRouter();
const { handleSubmit, errors, register, setError } = useForm();
const { changePassword, data, isLoading } = useChangePassword();
const [showPassword, setShowPassword] = useState({
password: "password",
newPassword: "password",
confirmPassword: "password",
});
const togglePassword = (key) => {
if (showPassword[key] === "password") {
setShowPassword({ ...showPassword, [key]: "text" });
} else {
setShowPassword({ ...showPassword, [key]: "password" });
}
};
const change = (data) => {
if (data.newPassword !== data.confirmPassword) {
return setError("confirmPassword", {
type: "manual",
message: "New password and confirm password does not match",
});
} else {
changePassword({
newPassword: data.newPassword,
currentPassword: data.currentPassword,
});
}
};
useEffect(() => {
if (data) router.push("/");
}, [data, router]);
return (
<ScaleFade in>
<Heading {...headingStyle}> Change password </Heading>
<Box alignSelf="flex-start" width="100%">
<Center>
<form className="form" onSubmit={handleSubmit(change)}>
<Stack width="100%" pt={4} spacing={3}>
<FormControl isInvalid={errors.newPassword}>
<InputGroup>
<Input
placeholder="Current password"
autoComplete="current-password"
name="currentPassword"
type={showPassword.password}
ref={register({
required: "Current password is required!",
})}
/>
<InputRightElement onClick={() => togglePassword("password")}>
<Icon icon="password" />
</InputRightElement>
</InputGroup>
<FormErrorMessage color="unsafe.400" pl="1">
{errors.newPassword && errors.newPassword.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.newPassword}>
<InputGroup>
<Input
autoComplete="new-password"
placeholder="New password"
name="newPassword"
type={showPassword.newPassword}
ref={register({ required: "Password is required!" })}
/>
<InputRightElement
onClick={() => togglePassword("newPassword")}
>
<Icon icon="password" />
</InputRightElement>
</InputGroup>
<FormErrorMessage color="unsafe.400" pl="1">
{errors.newPassword && errors.newPassword.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.confirmPassword}>
<InputGroup>
<Input
autoComplete="new-password"
placeholder="Confirm password"
name="confirmPassword"
type={showPassword.confirmPassword}
ref={register({ required: "Password is required!" })}
/>
<InputRightElement
onClick={() => togglePassword("confirmPassword")}
>
<Icon icon="password" />
</InputRightElement>
</InputGroup>
<FormErrorMessage color="unsafe.400" pl="1">
{errors.confirmPassword && errors.confirmPassword.message}
</FormErrorMessage>
</FormControl>
</Stack>
<Stack></Stack>
<Center>
<Button
my={8}
variant="solid"
colorScheme="primary"
type="submit"
isLoading={isLoading}
>
Save
</Button>
</Center>
</form>
</Center>
</Box>
</ScaleFade>
);
};
Security.getLayout = getLayout;
export default Security;

Wyświetl plik

@ -0,0 +1,597 @@
import React, { useState, useEffect, Suspense } from "react";
import {
Flex,
Heading,
Text,
Box,
Image,
ListItem,
Button,
Link,
ListIcon,
List,
useBreakpointValue,
Center,
Fade,
} from "@chakra-ui/react";
import { Grid, GridItem } from "@chakra-ui/react";
import { useUser, useAnalytics, useModals, useRouter } from "../src/core/hooks";
import { openPopupWidget, InlineWidget } from "react-calendly";
import TrustedBadge from "../src/components/TrustedBadge"
import { getLayout } from "../src/layouts";
const TEXT_PROPS = {
fontSize: ["lg", null, "xl"],
fontWeight: "600",
};
const HEADING_PROPS = {
fontWeight: "700",
fontSize: ["4xl", "5xl", "4xl", "5xl", "6xl", "7xl"],
};
const TITLE_PROPS = {
fontWeight: "700",
fontSize: ["4xl", "5xl", "5xl", "5xl", "6xl", "120px"],
};
const TRIPLE_PICS_PROPS = {
fontSize: ["2xl", "3xl", "3xl", "3xl", "4xl", "4xl"],
textAlign: "center",
fontWeight: "500",
py: 4,
};
const TRIPLE_PICS_TEXT = {
fontSize: ["lg", "xl", "xl", "xl", "2xl", "3xl"],
textAlign: "center",
fontWeight: "400",
mb: ["2rem", "2rem", "0", null, "0"],
};
const CARD_CONTAINER = {
className: "CardContainer",
w: "100%",
mx: [0, 0, "2rem", null, "4rem"],
alignSelf: ["center", null, "flex-start"],
};
const IMAGE_CONTAINER = {
className: "ImageContainer",
h: ["10rem", "14rem", "14rem", "15rem", "18rem", "20rem"],
justifyContent: "center",
};
const AWS_PATH = "https://s3.amazonaws.com/static.simiotics.com/landing";
const assets = {
background: `${AWS_PATH}/landing-background-2.png`,
aviator: `${AWS_PATH}/aviator-2.svg`,
icon1: `${AWS_PATH}/v2/Icon+1.svg`,
icon2: `${AWS_PATH}/v2/Icon+2.svg`,
icon3: `${AWS_PATH}/v2/Icon+3.svg`,
icon4: `${AWS_PATH}/v2/Icon+4.svg`,
icon5: `${AWS_PATH}/v2/Icon+5.svg`,
icon6: `${AWS_PATH}/v2/Icon+6.svg`,
activeloopLogo: `${AWS_PATH}/activeloop.svg`,
aiIncubeLogo: `${AWS_PATH}/ai incube.svg`,
b612Logo: `${AWS_PATH}/b612.svg`,
harvardLogo: `${AWS_PATH}/harvard.svg`,
mattermarkLogo: `${AWS_PATH}/mattermark.svg`,
mixrankLogo: `${AWS_PATH}/mixrank.svg`,
toolchainLogo: `${AWS_PATH}/toolchain.svg`,
};
const Homepage = () => {
const router = useRouter();
const buttonSize = useBreakpointValue({
base: "md",
sm: "md",
md: "md",
lg: "lg",
xl: "xl",
"2xl": "xl",
});
const ButtonRadius = "2xl";
const buttonWidth = ["100%", "100%", "40%", "45%", "45%", "45%"];
const buttonMinWidth = "10rem";
const { isInit } = useUser();
const { withTracking, MIXPANEL_EVENTS } = useAnalytics();
const { toggleModal } = useModals();
const [scrollDepth, setScrollDepth] = useState(0);
const getScrollPrecent = ({ currentTarget }) => {
const scroll_level =
(100 * (currentTarget.scrollTop + currentTarget.clientHeight)) /
currentTarget.scrollHeight;
return scroll_level;
};
const handleScroll = (e) => {
const currentScroll = Math.ceil(getScrollPrecent(e) / 10);
if (currentScroll > scrollDepth) {
withTracking(
setScrollDepth(currentScroll),
MIXPANEL_EVENTS.HOMEPAGE_SCROLL_DEPTH,
scrollDepth
);
}
};
const DoubleCTAButton = () => (
<Flex
justifyContent="flex-start"
// mt={20}
flexWrap="wrap"
width="100%"
>
<Button
variant="solid"
w={buttonWidth}
minW={buttonMinWidth}
borderRadius={ButtonRadius}
colorScheme="secondary"
size={buttonSize}
onClick={() => toggleModal("register")}
mr="1.25rem"
color="white"
border="2px solid #D35725"
fontWeight="400"
>
Sign up for free
</Button>
<Suspense fallback={""}>
<Button
variant="outline"
colorScheme="gray"
color="black"
borderRadius={ButtonRadius}
w={buttonWidth}
minW={buttonMinWidth}
mr="1.25rem"
size={buttonSize}
fontWeight="400"
onClick={() => {
openPopupWidget({
url: "https://calendly.com/neeraj-simiotics/bugout-30",
});
}}
>
Book office hours
</Button>
</Suspense>
</Flex>
);
useEffect(() => {
if (
router.nextRouter.asPath !== "/" &&
router.nextRouter.asPath.slice(0, 2) !== "/?"
) {
router.replace(router.nextRouter.asPath, undefined, {
shallow: true,
});
}
}, [isInit, router]);
return (
<Fade in>
<Box
width="100%"
flexDirection="column"
onScroll={(e) => handleScroll(e)}
>
<Flex
direction="column"
h="auto"
position="relative"
w="100%"
overflow="initial"
>
<Suspense fallback={""}></Suspense>
<Grid templateColumns="repeat(12,1fr)">
<GridItem px="7%" colSpan="12" pb={[1, 2, null, 8]}>
<Flex w="100%" wrap="wrap">
<Flex
direction="column"
alignItems="left"
pr={[0, null, 24]}
flexBasis="300px"
flexGrow={1}
>
<Heading
pt={["2rem", "3rem", "3rem", "10rem", "12rem", "14rem"]}
{...TITLE_PROPS}
mb={3}
fontWeight="700"
>
Measure the success of your dev tool
</Heading>
<Text
fontSize={["3xl", "4xl", "3xl", "4xl", "5xl", "6xl"]}
pb={[0, 0, 0, 0, 0, "3rem"]}
mb={20}
fontWeight="600"
color="primary.1000"
lineHeight="100%"
>
Get usage metrics and crash reports <br />{" "}
{`Improve your
users' experience`}
</Text>
<DoubleCTAButton />
</Flex>
<Flex flexBasis="200px" flexGrow={1} flexShrink={1}>
<Image
rel="preconnect"
src={assets["aviator"]}
alt="Bugout is on the fly to report your crashes"
/>
</Flex>
</Flex>
</GridItem>
<GridItem
px="7%"
colSpan="12"
pt={["20px", "20px", "100px", null, "120px"]}
pb={["20px", "56px", null, "184px"]}
bgSize="cover"
bgImage={`url(${assets["background"]})`}
>
<Heading
{...HEADING_PROPS}
textAlign="center"
pb={[12, 12, 12, null, 48]}
>
See what your users are experiencing with your library, API, or
command line tool
</Heading>
<Flex
direction={["column", null, "row"]}
flexWrap="nowrap"
justifyContent={["center", null, "space-evenly"]}
>
<Box {...CARD_CONTAINER}>
<Flex {...IMAGE_CONTAINER}>
<Image
w="100%"
src={assets["icon2"]}
alt="privacy is our prioriy"
/>
</Flex>
<Heading {...TRIPLE_PICS_PROPS}>
Catch and fix bugs faster
</Heading>
<Text {...TRIPLE_PICS_TEXT}>
Learn about errors as they occur, with full stack traces.
</Text>
</Box>
<Box {...CARD_CONTAINER}>
<Flex {...IMAGE_CONTAINER}>
<Image w="100%" src={assets["icon1"]} alt="live metrics" />
</Flex>
<Heading {...TRIPLE_PICS_PROPS}>
Understand your user engagement and retention
</Heading>
<Text {...TRIPLE_PICS_TEXT}>
Learn how users are using your tool, and how frequently.
</Text>
</Box>
<Box {...CARD_CONTAINER}>
<Flex {...IMAGE_CONTAINER}>
<Image
w="100%"
src={assets["icon3"]}
alt="we make it simple for user"
/>
</Flex>
<Heading {...TRIPLE_PICS_PROPS}>
Inform your product roadmap
</Heading>
<Text {...TRIPLE_PICS_TEXT}>
Understand which features people are actually using.
</Text>
</Box>
</Flex>
<Text
textAlign="center"
fontSize={["xl", "2xl", "2xl", "3xl", "4xl", "5xl"]}
fontWeight="600"
pt={[4, null, 12]}
>
We currently support Python, Javascript and Go!
<br />
Want us to support other programming languages?{" "}
<Button
size="2xl"
colorScheme="primary"
variant="link"
onClick={() => toggleModal("Integration")}
>
Let us know
</Button>
</Text>
</GridItem>
<GridItem px="7%" colSpan="12" pt="5.125rem" pb="66px">
<Heading {...HEADING_PROPS} textAlign="center" pb={12}>
Engage with your users on a deeper level
</Heading>
<Flex
direction={["column", null, "row"]}
flexWrap="nowrap"
justifyContent={["center", null, "space-evenly"]}
>
<Box {...CARD_CONTAINER}>
<Flex {...IMAGE_CONTAINER}>
<Image w="100%" src={assets["icon4"]} alt="live metrics" />
</Flex>
<Heading {...TRIPLE_PICS_PROPS}>Live dashboards</Heading>
<Text {...TRIPLE_PICS_TEXT}>
See your users journeys as they happen
</Text>
</Box>
<Box {...CARD_CONTAINER}>
<Flex {...IMAGE_CONTAINER}>
<Image
w="100%"
src={assets["icon5"]}
alt="privacy is our prioriy"
/>
</Flex>
<Heading {...TRIPLE_PICS_PROPS}>GDPR compliance</Heading>
<Text {...TRIPLE_PICS_TEXT}>
Automatically handle GDPR-related user requests
</Text>
</Box>
<Box {...CARD_CONTAINER}>
<Flex {...IMAGE_CONTAINER}>
<Image
w="100%"
src={assets["icon6"]}
alt="we make it simple for user"
/>
</Flex>
<Heading {...TRIPLE_PICS_PROPS}>
Simple user consent flows
</Heading>
<Text {...TRIPLE_PICS_TEXT}>
Define principled user consent flows in only a few lines of
code.
</Text>
</Box>
</Flex>
<Center>
<Flex
m={0}
mt="120px"
flexWrap="wrap"
width="100%"
w={["360px", "360px", "500px", null, "800px"]}
>
<Button
variant="solid"
w={buttonWidth}
minW="14rem"
borderRadius={ButtonRadius}
colorScheme="secondary"
size={buttonSize}
onClick={() => toggleModal("register")}
mr="1.25rem"
color="white"
border="2px solid #D35725"
fontWeight="400"
>
Sign up for free
</Button>
<Button
variant="outline"
colorScheme="gray"
color="black"
borderRadius={ButtonRadius}
w={buttonWidth}
minW="14rem"
mr="1.25rem"
size={buttonSize}
fontWeight="400"
onClick={() => {
openPopupWidget({
url: "https://calendly.com/neeraj-simiotics/bugout-30",
});
}}
>
Book office hours
</Button>
</Flex>
</Center>
</GridItem>
<GridItem
px="7%"
colSpan="12"
pt="66px"
bgColor="primary.50"
pb={["20px", "30px", "92px", null, "92px", "196px"]}
>
<Heading {...HEADING_PROPS} textAlign="center" pb={14} pt={0}>
Loved by proactive teams{" "}
<span role="img" aria-label="heart">
&#128153;
</span>
</Heading>
<Flex wrap="wrap" direction="row" justifyContent="center">
<Suspense fallback={""}>
<TrustedBadge
name="activeloop"
caseURL="/case-studies/activeloop"
ImgURL={assets["activeloopLogo"]}
/>
<TrustedBadge
name="ai incube"
ImgURL={assets["aiIncubeLogo"]}
/>
<TrustedBadge name="b612" ImgURL={assets["b612Logo"]} />
<TrustedBadge name="harvard" ImgURL={assets["harvardLogo"]} />
<TrustedBadge
name="mattermark"
ImgURL={assets["mattermarkLogo"]}
/>
<TrustedBadge name="mixrank" ImgURL={assets["mixrankLogo"]} />
<TrustedBadge
name="toolchain"
ImgURL={assets["toolchainLogo"]}
/>
</Suspense>
</Flex>
</GridItem>
<GridItem px="7%" colSpan="12" py={24}>
<Heading textAlign="center" {...HEADING_PROPS} pb={8}>
Ready to learn more?
</Heading>
<Text textAlign="center" {...TEXT_PROPS}>
Book office hours with Neeraj Kashyap (
<Link
color="primary.500"
isExternal
href="https://github.com/zomglings"
>
@zomglings
</Link>
), CEO of Bugout
</Text>
</GridItem>
<GridItem px="7%" colSpan="12"></GridItem>
</Grid>
<Flex
bg="primary.1200"
direction={["column", null, "row"]}
w="100%"
py="2rem"
px="7%"
h="100%"
>
<Flex
w={["100%", null, "50%"]}
textColor="white.200"
direction="column"
pr={[0, 0, 12, null, 12]}
>
<Heading
fontSize={["xl", "3xl", null, "3xl", "5xl", "5xl"]}
fontWeight="500"
alignSelf="left"
pt={[4, 4, 24, null, 24]}
pb={[6, 6, 12, null, 16]}
>
{`Let's talk about:`}
</Heading>
<List
fontWeight="400"
fontSize={["md", null, "2xl", "2xl", "3xl", "4xl"]}
alignSelf="center"
spacing={[4, 4, 8, null, 8]}
>
<ListItem>
<ListIcon as={() => "- "} />
How to measure and improve the quality of your users
experience
</ListItem>
<ListItem>
<ListIcon as={() => "- "} />
Ethical data collection
</ListItem>
<ListItem>
<ListIcon as={() => "- "} />
Developer tools best practices, pulling from our experience at
OpenAI and Google (TensorFlow, Kubeflow, Google Cloud)
</ListItem>
</List>
<Heading
fontSize={["xl", "3xl", null, "3xl", "5xl", "5xl"]}
fontWeight="500"
pt={[4, null, 8, 16, 24]}
>
Book now &#8594;
</Heading>
</Flex>
<Flex className="CalendlyWrapper" w={["100%", null, "45%"]}>
<InlineWidget
styles={{
width: "100%",
height: "720px",
}}
hid
url="https://calendly.com/neeraj-simiotics/bugout-30?hide_event_type_details=1"
/>
</Flex>
</Flex>
<Grid px="7%" templateColumns="repeat(12,1fr)">
<GridItem colSpan="4" py={2}>
&nbsp;
</GridItem>
<GridItem colSpan="4" py={2}>
<Center>
<iframe
title="substack"
src="https://bugout.substack.com/embed"
width="480"
height="320"
frameBorder="0"
scrolling="no"
></iframe>
</Center>
</GridItem>
<GridItem colSpan="4" py={2}>
&nbsp;
</GridItem>
</Grid>
</Flex>
</Box>
</Fade>
);
};
export async function getStaticProps() {
const metaTags = {
title: "Bugout: Measure the success of your dev tool",
description:
"Get usage metrics and crash reports. Improve your users' experience",
keywords:
"bugout, bugout-dev, bugout.dev, usage-metrics, analytics, dev-tool ,knowledge, docs, journal, entry, find-anything",
url: "https://bugout.dev",
image:
"https://s3.amazonaws.com/static.simiotics.com/landing/aviator-2.svg",
};
const assetPreload = Object.keys(assets).map((key) => {
return {
rel: "preload",
href: assets[key],
as: "image",
};
});
const preconnects = [
{ rel: "preconnect", href: "https://s3.amazonaws.com" },
{ rel: "preconnect", href: "https://assets.calendly.com/" },
];
const preloads = assetPreload.concat(preconnects);
return {
props: { metaTags, preloads },
};
}
Homepage.layout = "default";
Homepage.getLayout = getLayout;
export default Homepage;

Wyświetl plik

@ -1,8 +0,0 @@
import Head from 'next/head'
import Image from 'next/image'
export default function Home() {
return (
<div>hello world!</div>
)
}

Wyświetl plik

@ -0,0 +1,121 @@
import React from "react";
import { Flex, Link, HStack, Skeleton, Box, Title } from "@chakra-ui/react";
import { ExternalLinkIcon } from "@chakra-ui/icons";
import { useJournalEntry, useRouter } from "../../src/core/hooks";
import FourOFour from "../../src/components/FourOFour";
import FourOThree from "../../src/components/FourOThree";
import Tags from "../../src/components/Tags";
import CustomIcon from "../../src/components/CustomIcon";
import { getLayout } from "../../src/layouts/EntriesLayout";
import MarkdownView from "react-showdown";
const Entry = () => {
const router = useRouter();
const { entryId } = router.params;
const journalId = `9b0d7567-4634-4bf7-946d-60ef4414aa93`;
const {
data: entry,
isFetchedAfterMount,
isLoading,
isError,
error,
} = useJournalEntry(journalId, entryId);
const contextUrl = () => {
if (entry?.context_url) {
switch (entry.context_type) {
case "slack":
return (
<Link href={entry.context_url} isExternal>
<CustomIcon width="28px" icon="slack" />
</Link>
);
case "github":
return (
<Link href={entry.context_url} isExternal>
<CustomIcon width="28px" icon="github" />
</Link>
);
default:
return (
<Link href={entry.context_url} isExternal>
<ExternalLinkIcon bg="none" boxSize="18px" />
</Link>
);
}
} else return "";
};
if (isError && error.response.status === 404) return <FourOFour />;
if (isError && error.response.status === 403) return <FourOThree />;
// if (!entry || isLoading) return "";
return (
<Flex
id="Entry"
height="100%"
flexGrow="1"
flexDirection="column"
key={entryId}
>
<Skeleton
id="EntryNameSkeleton"
mx={2}
mt={2}
overflow="initial"
isLoaded={!isLoading}
>
<HStack id="EntryHeader" width="100%" m={0}>
<Box
id="ContextURL"
transition="0.3s"
_hover={{ transform: "scale(1.2)" }}
pl={2}
pr={entry?.context_url ? 2 : 0}
>
{contextUrl()}
</Box>
<Title
overflow="hidden"
width={entry?.context_url ? "calc(100% - 28px)" : "100%"}
// height="auto"
minH="36px"
style={{ marginLeft: "0" }}
m={0}
p={0}
fontWeight="600"
fontSize="1.5rem"
textAlign="left"
>
{entry?.title}
</Title>
</HStack>
</Skeleton>
<Skeleton
id="TagsSkeleton"
mx={2}
overflow="initial"
mt={1}
isLoaded={isFetchedAfterMount || entry}
>
<Tags entry={entry} />
</Skeleton>
<Skeleton
height="10px"
flexGrow={1}
id="EditorSkeleton"
mx={2}
mt={1}
isLoaded={isFetchedAfterMount || entry}
>
<MarkdownView
markdown={entry?.content}
options={{ tables: true, emoji: true }}
/>
</Skeleton>
</Flex>
);
};
Entry.getLayout = getLayout;
export default Entry;

Wyświetl plik

@ -0,0 +1,6 @@
import { getLayout } from "../../src/layouts/EntriesLayout";
const Entry = () => {
return "";
};
Entry.getLayout = getLayout;
export default Entry;

Wyświetl plik

@ -0,0 +1,112 @@
import { getLayout } from "../src/layouts/AppLayout";
import React, { useState } from "react";
import SubscriptionsList from "../src/components/SubscriptionsList";
import { useSubscriptions } from "../src/core/hooks";
import {
Box,
Center,
Spinner,
ScaleFade,
Heading,
Flex,
Button,
Modal,
useDisclosure,
ModalOverlay,
ModalContent,
} from "@chakra-ui/react";
import { headingStyle } from "./index";
import NewSubscription from "../src/components/NewSubscription";
import { AiOutlinePlusCircle } from "react-icons/ai";
const Subscriptions = () => {
const { subscriptionsCache } = useSubscriptions();
const { isOpen, onOpen, onClose } = useDisclosure();
const [isAddingFreeSubscription, setIsAddingFreeSubscription] = useState();
document.title = `My Subscriptions`;
const newSubscriptionClicked = (isForFree) => {
setIsAddingFreeSubscription(isForFree);
onOpen();
};
return (
<Box w="100%" px="7%" pt={2}>
<Modal
isOpen={isOpen}
onClose={onClose}
size="2xl"
scrollBehavior="outside"
>
<ModalOverlay />
<ModalContent>
<NewSubscription
isFreeOption={isAddingFreeSubscription}
onClose={onClose}
/>
</ModalContent>
</Modal>
{subscriptionsCache.isLoading ? (
<Center>
<Spinner
hidden={false}
// ref={loadMoreButtonRef}
my={8}
size="lg"
color="primary.500"
thickness="4px"
speed="1.5s"
/>
</Center>
) : (
<ScaleFade in>
<Heading {...headingStyle}> My Subscriptions </Heading>
<Flex
mt={4}
overflow="initial"
maxH="unset"
height="100%"
direction="column"
>
<Flex
h="3rem"
w="100%"
bgColor="primary.50"
borderTopRadius="xl"
justifyContent="flex-end"
alignItems="center"
>
{subscriptionsCache.data?.is_free_subscription_availible && (
<Button
onClick={() => newSubscriptionClicked(true)}
mr={8}
colorScheme="suggested"
variant="solid"
size="sm"
rightIcon={<AiOutlinePlusCircle />}
>
Add for free
</Button>
)}
<Button
onClick={() => newSubscriptionClicked(false)}
mr={8}
colorScheme="primary"
variant="solid"
size="sm"
rightIcon={<AiOutlinePlusCircle />}
>
Add new
</Button>
</Flex>
<SubscriptionsList />
</Flex>
</ScaleFade>
)}
</Box>
);
};
Subscriptions.getLayout = getLayout;
export default Subscriptions;

Wyświetl plik

@ -1,4 +1,4 @@
import { jsx } from "@emotion/react";
import React from "react";
import { ChakraProvider } from "@chakra-ui/react";
import theme from "./Theme/theme";
import {

Wyświetl plik

@ -24,7 +24,7 @@ const AccountIconButton = (props) => {
{...props}
as={IconButton}
aria-label="Account menu"
icon={<RiAccountCircleLine />}
icon={<RiAccountCircleLine size="26px"/>}
// variant="outline"
color="gray.100"
/>
@ -35,12 +35,6 @@ const AccountIconButton = (props) => {
m={0}
>
<MenuGroup>
<RouterLink href="/account/teams" passHref>
<MenuItem>Teams</MenuItem>
</RouterLink>
<RouterLink href="/account/tokens" passHref>
<MenuItem>Tokens</MenuItem>
</RouterLink>
<RouterLink href="/account/security" passHref>
<MenuItem>Security</MenuItem>
</RouterLink>

Wyświetl plik

@ -1,6 +1,4 @@
import { jsx } from "@emotion/react";
import { useState, useContext, useEffect } from "react";
import React, { useState, useContext, useEffect } from "react";
import RouterLink from "next/link";
import {
Flex,
@ -27,9 +25,8 @@ import {
ArrowLeftIcon,
ArrowRightIcon,
} from "@chakra-ui/icons";
import { IoIosJournal } from "react-icons/io";
import { MdTimeline } from "react-icons/md";
import useRouter from "../core/hooks/useRouter";
import SearchBar from "./SearchBar";
import UIContext from "../core/providers/UIProvider/context";
import AccountIconButton from "./AccountIconButton";
@ -101,48 +98,15 @@ const AppNavbar = () => {
{!ui.isMobileView && (
<>
<Flex
minW={["100%", "50%", "60%", "80%", null, "80%"]}
width="100%"
id="SearchBarwButtons"
position="relative"
alignItems="baseline"
// bgColor="unsafe.100"
justifyContent="flex-end"
>
<IconButton
ml={4}
colorScheme="blue"
aria-label="App navigation"
icon={<IoIosJournal />}
onClick={() => {
ui.isMobileView
? ui.setSidebarToggled(!ui.sidebarToggled)
: ui.setSidebarVisible(!ui.sidebarVisible);
}}
/>
<SearchBar
pl={4}
position="absolute"
left="64px"
w={
isSearchBarActive
? [
"100%",
"calc(100% - 80px)",
"calc(100% - 0px)",
"calc(100% - 0px)",
null,
"100%",
]
: ["64px", "64px", "50%", "55%", null, "60%"]
}
h="2rem"
bgColor="primary.1200"
alignSelf="center"
transition="0.5s"
/>
{/* <Fade in={!ui.searchBarActive}> */}
{!ui.isMobileView && (
<ButtonGroup
position="relative"
// position="relative"
left={
isSearchBarActive
? "100%"
@ -154,8 +118,6 @@ const AppNavbar = () => {
colorScheme="secondary"
spacing={4}
px={2}
justifyContent="space-between"
width={["64px", "70%", "60%", "45%", null, "40%"]}
zIndex={ui.searchBarActive ? -10 : 0}
size={["xs", "xs", "xs", "lg", null, "lg"]}
>
@ -169,13 +131,9 @@ const AppNavbar = () => {
Product
</Button>
</RouterLink>
{/* <Button>Explore</Button> */}
{/* <Button>Docs</Button> */}
</ButtonGroup>
)}
{/* </Fade> */}
</Flex>
{/* <Spacer /> */}
<Flex justifyContent="flex-end" width="30%" pr={2}>
<IconButton
@ -211,8 +169,6 @@ const AppNavbar = () => {
size="lg"
h="32px"
/>
{/* <IconButton>Explore</IconButton>
<IconButton>Docs</IconButton> */}
</Flex>
</>
)}
@ -227,7 +183,6 @@ const AppNavbar = () => {
h="32px"
m={0}
size={iconSize}
// size={["md", "lg", null, "md"]}
colorScheme="gray"
aria-label="App navigation"
icon={<HamburgerIcon />}
@ -238,28 +193,19 @@ const AppNavbar = () => {
}}
/>
)}
<SearchBar
pl={4}
// position="absolute"
w={
isSearchBarActive
? [
"100%",
"calc(100%)",
"calc(100% - 64px)",
"calc(100% - 164px)",
null,
"100%",
]
: ["32px", "32px", "40%", "55%", null, "60%"]
}
h="2rem"
px={isSearchBarActive ? 2 : 0}
bgColor="primary.1200"
alignSelf="center"
transition="0.5s"
/>
<RouterLink href="/stream" passHref>
<IconButton
m={0}
variant="link"
justifyContent="space-evenly"
alignContent="center"
h="32px"
size={iconSize}
colorScheme="gray"
aria-label="go to ticker"
icon={<MdTimeline />}
/>
</RouterLink>
{!isSearchBarActive && (
<IconButton
m={0}

Wyświetl plik

@ -1,40 +0,0 @@
import { jsx } from "@emotion/react";
import { Box, Flex} from "@chakra-ui/react";
import React, { Fragment } from "react";
import LoadingDots from "./LoadingDots"
const AppSidebar = () => {
if (false) {
return (
<LoadingDots
fontSize="md"
textColor="white"
py={4}
px={4}
isActive={true}
>
Reticulating splines
</LoadingDots>
);
}
return (
<Fragment>
<Flex
className="ScrollableWrapper"
flexGrow={1}
overflow="hidden"
direction="column"
pb={8}
>
<Box className="Scrollable" id="JournalList" overflowY="scroll">
</Box>
</Flex>
</Fragment>
);
};
export default AppSidebar;

Wyświetl plik

@ -1,30 +0,0 @@
import { jsx } from "@emotion/react";
import { Circle, Text } from "@chakra-ui/react";
const CircleButton = ({ sign, onClick }) => {
return (
<Circle
overflow="initial"
transition="0.5s"
_hover={{ bgColor: "primary.100", transform: "scale(1.2)" }}
boxSize="28px"
m="0 0.5rem"
cursor="pointer"
background="primary.800"
boxShadow="0 4px 14px 0 rgba(33, 41, 144, 0.3)"
// box-shadow: 0 4px 14px 0 rgba(33, 41, 144, 0.3);
onClick={onClick}
>
<Text
transition="1s"
_hover={{ transform: "scale(1.5)" }}
color="white.100"
>
{sign}
</Text>
</Circle>
);
};
export default CircleButton;

Wyświetl plik

@ -1,6 +1,4 @@
import { jsx } from "@emotion/react";
import { Fragment } from "react";
import React, { Fragment } from "react";
import {
Popover,
PopoverTrigger,

Wyświetl plik

@ -1,78 +0,0 @@
import { jsx } from "@emotion/react";
import { Fragment, useCallback } from "react";
import {
Popover,
PopoverTrigger,
PopoverContent,
PopoverCloseButton,
PopoverHeader,
PopoverFooter,
PopoverBody,
Button,
} from "@chakra-ui/react";
import { useDeleteEntry, useRouter } from "../core/hooks";
const DeleteEntryButton = ({ id, journalId, appScope }) => {
const router = useRouter();
const deleteEntry = useDeleteEntry({
entryId: id,
journalId,
});
const onConfirm = useCallback(
(onClose) => () => {
deleteEntry();
onClose();
setTimeout(() => {
router.push({
pathname: `/app/${appScope}/${journalId}/entries`,
query: router.query,
});
}, 1000);
},
[deleteEntry, journalId, appScope, router]
);
return (
<Popover usePortal>
{({ onClose }) => (
<Fragment>
<PopoverTrigger>
<Button size="xs" variant="link" colorScheme="primary" ml={1}>
Delete
</Button>
</PopoverTrigger>
<PopoverContent zIndex={100} bg="White">
<PopoverCloseButton />
<PopoverHeader fontWeight="bold">Please confirm!</PopoverHeader>
<PopoverBody fontSize="md">
Are you sure you want to delete the entry ?
</PopoverBody>
<PopoverFooter>
<Button
onClick={onClose}
colorScheme="primary"
variant="outline"
size="sm"
>
No
</Button>
<Button
onClick={onConfirm(onClose)}
colorScheme="unsafe"
variant="solid"
size="sm"
>
Yes
</Button>
</PopoverFooter>
</PopoverContent>
</Fragment>
)}
</Popover>
);
};
export default DeleteEntryButton;

Wyświetl plik

@ -1,65 +0,0 @@
import { jsx } from "@emotion/react";
import {
Button,
AlertDialog,
AlertDialogBody,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogContent,
AlertDialogOverlay,
} from "@chakra-ui/react";
import { useRouter, useDeleteJournal } from "../core/hooks";
const DeleteJournalAlert = ({ isOpen, toggleSelf, cancelRef }) => {
const router = useRouter();
const { id } = router.params;
const { deleteJournal } = useDeleteJournal(id);
const deleteJournalConfirm = () => {
deleteJournal(id);
toggleSelf(false);
router.replace("/app/personal/");
};
return (
<AlertDialog
isOpen={isOpen}
leastDestructiveRef={cancelRef}
onClose={() => toggleSelf(false)}
>
<AlertDialogOverlay backgroundColor="white.50">
<AlertDialogContent bg="solid" backgroundColor="white.100">
<AlertDialogHeader fontSize="lg" fontWeight="bold">
Delete this journal
</AlertDialogHeader>
<AlertDialogBody>
{`Are you sure? You can't undo this action afterwards.`}
</AlertDialogBody>
<AlertDialogFooter>
<Button
ref={cancelRef}
onClick={() => toggleSelf(false)}
variant="outline"
colorScheme="primary"
>
Cancel
</Button>
<Button
variant="solid"
colorScheme="unsafe"
onClick={() => deleteJournalConfirm()}
ml={3}
>
Delete
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialogOverlay>
</AlertDialog>
);
};
export default DeleteJournalAlert;

Wyświetl plik

@ -1,21 +1,7 @@
import { jsx } from "@emotion/react";
import { useState, useRef, useEffect, useContext } from "react";
import {
Box,
Flex,
Spinner,
Container,
Button,
VStack,
Center,
} from "@chakra-ui/react";
import {
useJournalEntries,
useRouter,
useJournalPermissions,
} from "../core/hooks";
import { EntryList, NewEntryModal } from ".";
import React, { useRef, useEffect, useContext } from "react";
import { Box, Flex, Spinner, Button, Center } from "@chakra-ui/react";
import { useJournalEntries, useJournalPermissions } from "../core/hooks";
import EntryList from "./EntryList";
import UIContext from "../core/providers/UIProvider/context";
const pageSize = 25;
@ -23,15 +9,16 @@ const isContent = false;
const EntriesNavigation = () => {
const ui = useContext(UIContext);
const router = useRouter();
const { currentUserPermissions: permissions } = useJournalPermissions(
router.params.id,
router.params.appScope
`9b0d7567-4634-4bf7-946d-60ef4414aa93`,
`personal`
);
const loadMoreButtonRef = useRef(null);
const { id: journalId, appScope } = router.params;
const journalId = `9b0d7567-4634-4bf7-946d-60ef4414aa93`;
const appScope = `personal`;
const {
fetchMore,
@ -48,7 +35,6 @@ const EntriesNavigation = () => {
isContent,
searchQuery: ui.searchTerm,
});
const [newEntryModal, toggleNewEntryModal] = useState();
const handleScroll = ({ currentTarget }) => {
if (
@ -68,7 +54,7 @@ const EntriesNavigation = () => {
}, [journalId, ui.searchTerm, refetch, setSearchTerm]);
const entriesPagesData = EntriesPages
? EntriesPages.map((page) => {
? EntriesPages.pages.map((page) => {
return page.data;
})
: [""];
@ -90,23 +76,6 @@ const EntriesNavigation = () => {
direction="column"
flexGrow={1}
>
<Flex align="center" height={12} borderColor="white.300">
{canCreate && (
<Button
width="100%"
variant="solid"
colorScheme="secondary"
alignSelf="center"
height={12}
m={0}
borderRadius={0}
// mb={2}
onClick={() => toggleNewEntryModal(true)}
>
New Entry
</Button>
)}
</Flex>
{entries && !isLoading ? (
<Flex
className="ScrollableWrapper"
@ -133,22 +102,6 @@ const EntriesNavigation = () => {
disableCopy={!canCreate}
/>
))}
{entries.length === 0 && (
<Center>
<VStack>
<Container pt={8}>
This journal has no entries so far.{" "}
</Container>
<Button
onClick={() => toggleNewEntryModal(true)}
variant="outline"
colorScheme="suggested"
>
Create one
</Button>
</VStack>
</Center>
)}
{canFetchMore && !isFetchingMore && (
<Center>
<Button
@ -186,13 +139,6 @@ const EntriesNavigation = () => {
/>
</Center>
)}
{newEntryModal && (
<NewEntryModal
toggleModal={toggleNewEntryModal}
journalId={journalId}
/>
)}
</Box>
);
};

Wyświetl plik

@ -0,0 +1,123 @@
import React, { useContext } from "react";
import { Flex, Link, HStack, Skeleton, Box, Title } from "@chakra-ui/react";
import { ExternalLinkIcon } from "@chakra-ui/icons";
import { useJournalEntry } from "../../src/core/hooks";
import FourOFour from "./FourOFour";
import FourOThree from "./FourOThree";
import Tags from "./Tags";
import CustomIcon from "./CustomIcon";
import MarkdownView from "react-showdown";
import UIContext from "../core/providers/UIProvider/context";
const Entry = () => {
const ui = useContext(UIContext);
const entryId = ui.entryId;
const journalId = `9b0d7567-4634-4bf7-946d-60ef4414aa93`;
const appScope = `personal`;
const {
data: entry,
isFetchedAfterMount,
isLoading,
isError,
error,
} = useJournalEntry(journalId, entryId, appScope);
const contextUrl = () => {
if (entry?.context_url) {
switch (entry.context_type) {
case "slack":
return (
<Link href={entry.context_url} isExternal>
<CustomIcon width="28px" icon="slack" />
</Link>
);
case "github":
return (
<Link href={entry.context_url} isExternal>
<CustomIcon width="28px" icon="github" />
</Link>
);
default:
return (
<Link href={entry.context_url} isExternal>
<ExternalLinkIcon bg="none" boxSize="18px" />
</Link>
);
}
} else return "";
};
if (isError && error.response.status === 404) return <FourOFour />;
if (isError && error.response.status === 403) return <FourOThree />;
// if (!entry || isLoading) return "";
return (
<Flex
id="Entry"
height="520px"
flexGrow="1"
flexDirection="column"
key={entryId}
bgColor="white"
p={2}
overflowY="scroll"
>
<Skeleton
id="EntryNameSkeleton"
mx={2}
mt={2}
overflow="initial"
isLoaded={!isLoading}
>
<HStack id="EntryHeader" width="100%" m={0}>
<Box
id="ContextURL"
transition="0.3s"
_hover={{ transform: "scale(1.2)" }}
pl={2}
pr={entry?.context_url ? 2 : 0}
>
{contextUrl()}
</Box>
<Title
overflow="hidden"
width={entry?.context_url ? "calc(100% - 28px)" : "100%"}
// height="auto"
minH="36px"
style={{ marginLeft: "0" }}
m={0}
p={0}
fontWeight="600"
fontSize="1.5rem"
textAlign="left"
>
{entry?.title}
</Title>
</HStack>
</Skeleton>
<Skeleton
id="TagsSkeleton"
mx={2}
overflow="initial"
mt={1}
isLoaded={isFetchedAfterMount || entry}
>
<Tags entry={entry} />
</Skeleton>
<Skeleton
height="10px"
flexGrow={1}
id="EditorSkeleton"
mx={2}
mt={1}
isLoaded={isFetchedAfterMount || entry}
>
<MarkdownView
markdown={entry?.content}
options={{ tables: true, emoji: true }}
/>
</Skeleton>
</Flex>
);
};
export default Entry;

Wyświetl plik

@ -1,67 +1,52 @@
import { jsx } from "@emotion/react";
import { Fragment, useContext } from "react";
import { Flex, Heading, Text, LinkBox } from "@chakra-ui/react";
import React, { useContext } from "react";
import { Flex, Heading, Text, IconButton } from "@chakra-ui/react";
import moment from "moment";
import { EntryCard, TagsList, DeleteEntryButton, CopyEntryButton } from ".";
import { ViewIcon } from "@chakra-ui/icons";
import { useRouter } from "../core/hooks";
import RouterLink from "next/link";
import UIContext from "../core/providers/UIProvider/context";
const EntryList = ({ entry, disableDelete, disableCopy }) => {
const EntryList = ({ entry }) => {
const ui = useContext(UIContext);
const router = useRouter();
const { id: journalId, entryId, appScope } = router.params;
const handleViewClicked = (entryId) => {
ui.setEntryId(entryId);
ui.setEntriesViewMode("entry");
router.push({
pathname: `/stream/${entry.id}`,
query: router.query,
});
};
return (
<RouterLink
href={{
pathname: `/app/${appScope}/${journalId}/entries/${entry.id}`,
query: router.query,
}}
passHref
<Flex
px={6}
borderTop="1px"
borderColor="white.300"
transition="0.1s"
_hover={{ bg: "secondary.200" }}
width="100%"
direction="row"
justifyContent="normal"
alignItems="baseline"
>
<LinkBox
onClick={() =>
ui.setEntriesViewMode(ui.isMobileView ? "entry" : "split")
}
>
<EntryCard isActive={entryId === entry.id ? true : false}>
<Heading as="h3" fontWeight="500" fontSize="md">
{entry.title}
</Heading>
<Flex alignItems="baseline">
<Text opacity="0.5" fontSize="xs">
{moment(entry.created_at).format("DD MMM, YYYY")}{" "}
</Text>
{entryId === entry.id && (
<Fragment>
{!disableDelete && (
<DeleteEntryButton
id={entryId}
journalId={journalId}
appScope={appScope}
/>
)}
{!disableCopy && (
<CopyEntryButton id={entryId} journalId={journalId} />
)}
</Fragment>
)}
</Flex>
<Flex align="start">
<Flex
width="100%"
mt={2}
align="center"
justifyContent="space-between"
>
<TagsList tags={entry.tags} />
</Flex>
</Flex>
</EntryCard>
</LinkBox>
</RouterLink>
<Flex flexGrow={1}>
<Heading as="h3" fontWeight="500" fontSize="md">
{entry.title}
</Heading>
</Flex>
<Text opacity="0.5" fontSize="xs" alignSelf="baseline">
{moment(entry.created_at).format("DD MMM, YYYY, h:mm:ss")}{" "}
</Text>
<IconButton
p={0}
variant="ghost"
boxSize="32px"
colorScheme="primary"
icon={<ViewIcon />}
onClick={() => handleViewClicked(entry.id)}
/>
</Flex>
);
};

Wyświetl plik

@ -1,6 +1,4 @@
import { jsx } from "@emotion/react";
import { Fragment, useContext } from "react";
import React, { Fragment, useContext } from "react";
import RouterLink from "next/link";
import {
Flex,
@ -14,12 +12,12 @@ import {
MenuItem,
MenuGroup,
MenuDivider,
IconButton,
Link,
} from "@chakra-ui/react";
import { ChevronDownIcon, HamburgerIcon } from "@chakra-ui/icons";
import { ChevronDownIcon } from "@chakra-ui/icons";
import useModals from "../core/hooks/useModals";
import UIContext from "../core/providers/UIProvider/context";
import ChakraAccountIconButton from "./AccountIconButton";
const LandingNavbar = () => {
const ui = useContext(UIContext);
@ -50,7 +48,7 @@ const LandingNavbar = () => {
>
<Spacer />
{ui.isLoggedIn && (
<RouterLink href="/app" passHref>
<RouterLink href="/stream" passHref>
<Button
as={Link}
colorScheme="secondary"
@ -84,41 +82,7 @@ const LandingNavbar = () => {
Log in
</Button>
)}
<RouterLink href="/pricing" passHref>
<Button color="white" fontWeight="400">
Pricing
</Button>
</RouterLink>
<RouterLink href="/case-studies/activeloop" passHref>
<Button color="white" fontWeight="400" as={Link}>
Case study
</Button>
</RouterLink>
<Menu>
<MenuButton
size="xl"
aria-label="menu"
as={IconButton}
color="white"
icon={<HamburgerIcon />}
>
About us
</MenuButton>
<MenuList
w={["100%", "16rem", "18rem", "20rem", "22rem", "24rem"]}
>
<RouterLink href="/team" passHref>
<MenuItem>Team and careers</MenuItem>
</RouterLink>
<RouterLink href="/events" passHref>
<MenuItem>Bugout events</MenuItem>
</RouterLink>
<MenuItem as="a" href="http://blog.bugout.dev">
Blog
</MenuItem>
</MenuList>
</Menu>
{ui.isLoggedIn && <ChakraAccountIconButton />}
</ButtonGroup>
</Fragment>
)}
@ -149,7 +113,7 @@ const LandingNavbar = () => {
>
<MenuGroup>
{ui.isLoggedIn && (
<RouterLink href="/app" passHref>
<RouterLink href="/stream" passHref>
<MenuItem bgColor="secondary.600">Open App</MenuItem>
</RouterLink>
)}

Wyświetl plik

@ -1,73 +0,0 @@
import { jsx } from "@emotion/react";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import {
Heading,
Box,
FormControl,
FormErrorMessage,
InputGroup,
Input,
Button,
} from "@chakra-ui/react";
import { Modal } from ".";
import { useCreateEntry, useRouter } from "../core/hooks";
const NewEntryModal = ({ toggleModal, journalId }) => {
const router = useRouter();
const { handleSubmit, errors, register } = useForm();
const { createEntry, isLoading, data } = useCreateEntry(journalId);
useEffect(() => {
if (!data) {
return;
}
if (data.data?.id) {
router.push({
pathname: `/app/personal/${journalId}/entries/${data.data.id}`,
query: { ...router.query, mode: "write" },
});
}
toggleModal(null);
}, [data, toggleModal, journalId, router]);
return (
<Modal onClose={() => toggleModal(null)}>
<Heading mt={2} as="h2" fontSize={["lg", "2xl"]}>
Create Entry
</Heading>
<form onSubmit={handleSubmit(createEntry)}>
<FormControl position="relative" isInvalid={errors.title}>
<InputGroup pt={4} width="100%">
<Input
colorScheme="primary"
variant="filled"
placeholder="Entry Title"
name="title"
ref={register({ required: "title is required!" })}
/>
</InputGroup>
<FormErrorMessage color="unsafe.400" pl="1">
{errors.title && errors.title.message}
</FormErrorMessage>
</FormControl>
<Box height="1px" width="100%" background="#eaebf8" mb="1.875rem" />
<Button
mt={8}
variant="solid"
colorScheme="primary"
type="submit"
size="lg"
width="100%"
isLoading={isLoading}
>
Create
</Button>
</form>
</Modal>
);
};
export default NewEntryModal;

Wyświetl plik

@ -0,0 +1,113 @@
import React, { useState, useEffect } from "react";
import { useSubscriptions } from "../core/hooks";
import {
Input,
Stack,
Text,
HStack,
useRadioGroup,
FormControl,
FormErrorMessage,
ModalBody,
ModalCloseButton,
ModalHeader,
Button,
ModalFooter,
Spinner,
} from "@chakra-ui/react";
import RadioCard from "./RadioCard";
import { useForm } from "react-hook-form";
const NewSubscription = ({ isFreeOption, onClose }) => {
const { typesCache, createSubscription } = useSubscriptions();
const { handleSubmit, errors, register } = useForm();
const [radioState, setRadioState] = useState("ethereum_blockchain");
let { getRootProps, getRadioProps, ref } = useRadioGroup({
name: "type",
defaultValue: radioState,
onChange: setRadioState,
});
const group = getRootProps();
useEffect(() => {
if (createSubscription.isSuccess) {
onClose();
}
}, [createSubscription.isSuccess, onClose]);
if (typesCache.isLoading) return <Spinner />;
const createSubscriptionWrap = (props) => {
createSubscription.mutate({
...props,
type: isFreeOption ? "free" : radioState,
});
};
return (
<form onSubmit={handleSubmit(createSubscriptionWrap)}>
<ModalHeader>Subscribe to a new address</ModalHeader>
<ModalCloseButton />
<ModalBody>
<FormControl isInvalid={errors.address}>
<Input
placeholder="new address"
name="address"
ref={register({ required: "address is required!" })}
></Input>
<FormErrorMessage color="unsafe.400" pl="1">
{errors.address && errors.address.message}
</FormErrorMessage>
</FormControl>
<Stack my={16} direction="column">
<Text fontWeight="600">
{isFreeOption
? `Free subscription is only availible Ethereum blockchain source`
: `On which source?`}
</Text>
<FormControl isInvalid={errors.type}>
<HStack {...group} alignItems="stretch">
{typesCache.data.map((type) => {
const radio = getRadioProps({
value: type.subscription_type,
isDisabled:
!type.active ||
(isFreeOption &&
type.subscription_type !== "ethereum_blockchain"),
});
if (!type.subscription_plan_id) return "";
return (
<RadioCard
onClick={() => console.log("hello")}
key={`subscription-type-${type.id}`}
{...radio}
>
{type.name}
</RadioCard>
);
})}
</HStack>
</FormControl>
</Stack>
<Input
placeholder="Add some notes"
name="note"
ref={register()}
></Input>
</ModalBody>
<ModalFooter>
<Button
type="submit"
colorScheme="suggested"
isLoading={createSubscription.isLoading}
>
Confirm
</Button>
<Button colorScheme="gray">Cancel</Button>
</ModalFooter>
</form>
);
};
export default NewSubscription;

Wyświetl plik

@ -1,483 +0,0 @@
import { jsx } from "@emotion/react";
import { useState } from "react";
import {
Checkbox,
VStack,
Table,
Tr,
Td,
Th,
Tbody,
Thead,
HStack,
Select,
FormControl,
FormErrorMessage,
Button,
Box,
Text,
Flex,
Link,
} from "@chakra-ui/react";
import { IconButton } from ".";
import { CloseIcon, DeleteIcon } from "@chakra-ui/icons";
import { useForm } from "react-hook-form";
import { TiGroupOutline } from "react-icons/ti";
import { IoIosJournal } from "react-icons/io";
import { useRouter, useGroups, useJournalPermissions } from "../core/hooks";
import RouterLink from "next/link";
const PermissionTable = ({ id, user, LoadingSpinner, setDeleteAlert }) => {
const router = useRouter();
const { handleSubmit, errors, register } = useForm();
const [showNewHolderForm, toggleNewHolder] = useState(false);
const { data: groups } = useGroups();
const { appScope } = router.params;
const {
holders,
error,
setJournalPermissionMutation,
removeJournalPermissionMutation,
currentUserPermissions,
} = useJournalPermissions(id, "personal");
const checkboxToggled = (holder, value) => {
return holder.permissions.includes(value)
? removePermissions({
holder_id: holder.holder_id,
holder_type: holder.holder_type,
permission_list: [value],
})
: addPermissions({
holder_id: holder.holder_id,
holder_type: holder.holder_type,
permission_list: [value],
});
};
if (error) {
return (
<Box>
It seems you have no permissions to open this page
<RouterLink
href={{
pathname: `/app/${appScope}/${id}`,
query: router.query,
}}
passHref
>
<Button
as={Link}
m={8}
variant="solid"
colorScheme="primary"
leftIcon={<IoIosJournal />}
>
Back to the journal
</Button>
</RouterLink>
</Box>
);
}
if (!currentUserPermissions || !holders || !groups) return <LoadingSpinner />;
const removePermissions = (formData) => {
formData.permission_list = formData.permission_list.filter(Boolean);
removeJournalPermissionMutation.removeJournalPermission({
holder_type: "group",
...formData,
});
};
const addPermissions = (formData) => {
formData.permission_list = formData.permission_list.filter(Boolean);
toggleNewHolder(false);
setJournalPermissionMutation.setJournalPermission({
holder_type: "group",
...formData,
});
};
if (
!currentUserPermissions.includes("journals.update") &&
showNewHolderForm
) {
toggleNewHolder(false);
}
return (
<VStack>
<form onSubmit={handleSubmit(addPermissions)}>
<Table
width="100%"
variant="simple"
colorScheme="primary"
p={0}
m={0}
textColor="inherit"
>
<Thead>
<Tr>
<Th
textColor="inherit"
borderRightWidth="1px"
textAlign="center"
colSpan="2"
>
Holder
</Th>
<Th borderRightWidth="1px" textAlign="center" colSpan="3">
Journals
</Th>
<Th borderRightWidth="1px" textAlign="center" colSpan="4">
Entries
</Th>
<Th>Actions</Th>
</Tr>
<Tr>
<Th textAlign="center" width="200px" px={1}>
name
</Th>
<Th textAlign="center" px={1} borderRightWidth="1px">
type
</Th>
<Th textAlign="center" px={1}>
Read
</Th>
<Th textAlign="center" px={1}>
Update
</Th>
<Th textAlign="center" px={1} borderRightWidth="1px">
Delete
</Th>
<Th textAlign="center" px={1}>
Read
</Th>
<Th textAlign="center" px={1}>
Create
</Th>
<Th textAlign="center" px={1}>
Update
</Th>
<Th textAlign="center" px={1}>
Delete
</Th>
<Th textAlign="center" width="140px" px={1}></Th>
</Tr>
</Thead>
<Tbody>
{holders.map((holder, idx) => {
const index = groups.findIndex(
(item) => item.group_id === holder.holder_id
);
const name =
index !== -1
? groups[index]?.group_name
: holder.holder_id === user?.user_id
? user?.username
: null;
return (
<Tr
bgColor={name ? "white.100" : "gray.50"}
key={`row-${idx}`}
borderColor="white.200"
>
<Td textAlign="center">
{name ? (
name
) : (
<Text fontSize="xx-small">{holder.holder_id}</Text>
)}
</Td>
<Td textAlign="center">{holder.holder_type}</Td>
<Td>
<Checkbox
isDisabled={
!name ||
!currentUserPermissions.includes("journals.update")
}
onChange={() => checkboxToggled(holder, "journals.read")}
isChecked={holder.permissions.includes("journals.read")}
></Checkbox>
</Td>
<Td>
<Checkbox
isDisabled={
!name ||
!currentUserPermissions.includes("journals.update")
}
isChecked={holder.permissions.includes("journals.update")}
onChange={() =>
checkboxToggled(holder, "journals.update")
}
></Checkbox>
</Td>
<Td>
<Checkbox
isDisabled={
!name ||
!currentUserPermissions.includes("journals.update")
}
isChecked={holder.permissions.includes("journals.delete")}
onChange={() =>
checkboxToggled(holder, "journals.delete")
}
></Checkbox>
</Td>
<Td>
<Checkbox
isDisabled={
!name ||
!currentUserPermissions.includes("journals.update")
}
isChecked={holder.permissions.includes(
"journals.entries.read"
)}
onChange={() =>
checkboxToggled(holder, "journals.entries.read")
}
></Checkbox>
</Td>
<Td>
<Checkbox
isDisabled={
!name ||
!currentUserPermissions.includes("journals.update")
}
isChecked={holder.permissions.includes(
"journals.entries.create"
)}
onChange={() =>
checkboxToggled(holder, "journals.entries.create")
}
></Checkbox>
</Td>
<Td>
<Checkbox
isDisabled={
!name ||
!currentUserPermissions.includes("journals.update")
}
isChecked={holder.permissions.includes(
"journals.entries.update"
)}
onChange={() =>
checkboxToggled(holder, "journals.entries.update")
}
></Checkbox>
</Td>
<Td>
<Checkbox
isDisabled={
!name ||
!currentUserPermissions.includes("journals.update")
}
isChecked={holder.permissions.includes(
"journals.entries.delete"
)}
onChange={() =>
checkboxToggled(holder, "journals.entries.delete")
}
></Checkbox>
</Td>
<Td>
<IconButton
hidden={
!name ||
!currentUserPermissions.includes("journals.update")
}
onClick={() =>
removePermissions({
holder_id: holder.holder_id,
holder_typ: holder.holder_type,
permission_list: [
"journals.read",
"journals.delete",
"journals.entries.read",
"journals.entries.create",
"journals.entries.update",
"journals.entries.delete",
"journals.update",
],
})
}
icon={<CloseIcon />}
/>
</Td>
</Tr>
);
})}
{showNewHolderForm && (
<Tr width="100%">
<Td>
<FormControl isInvalid={errors.groupName}>
<Select
_focus={{
outline: "solid 1px",
outlineColor: "primary.500",
}}
fontSize="sm"
border="none"
placeholder="Select a team"
name="holder_id"
// width="200px"
height="fit-content"
bgColor="white.200"
ref={(e) => {
register(e, {
required: "Please select a group",
});
}}
>
{groups.map((group, idx) => {
if (
!holders.some((i) => i.holder_id === group.group_id)
) {
return (
<option key={idx} value={group.group_id}>
{group.group_name}
</option>
);
}
return null;
})}
</Select>
<FormErrorMessage color="unsafe.400" pl="1">
{errors.groupName && errors.groupName.message}
</FormErrorMessage>
</FormControl>
</Td>
<Td>{/* <Checkbox /> */}</Td>
<Td>
<Checkbox
type="checkbox"
name="permission_list[0]"
value="journals.read"
defaultValue={null}
defaultChecked={false}
ref={register}
/>
</Td>
<Td>
<Checkbox
type="checkbox"
name="permission_list[1]"
value="journals.update"
defaultChecked={false}
ref={register}
/>
</Td>
<Td>
<Checkbox
type="checkbox"
name="permission_list[2]"
value="journals.delete"
defaultChecked={false}
ref={register}
/>
</Td>
<Td>
<Checkbox
type="checkbox"
name="permission_list[3]"
value="journals.entries.read"
defaultChecked={false}
ref={register}
/>
</Td>
<Td>
<Checkbox
type="checkbox"
name="permission_list[4]"
value="journals.entries.create"
defaultChecked={false}
ref={register}
/>
</Td>
<Td>
<Checkbox
type="checkbox"
name="permission_list[5]"
value="journals.entries.update"
defaultChecked={false}
ref={register}
/>
</Td>
<Td>
<Checkbox
type="checkbox"
name="permission_list[6]"
value="journals.entries.delete"
defaultChecked={false}
ref={register}
/>
</Td>
<Td>
<HStack>
<IconButton
onClick={() => toggleNewHolder(false)}
icon={<CloseIcon />}
/>
<IconButton type="submit" />
</HStack>
</Td>
</Tr>
)}
</Tbody>
</Table>
</form>
<Flex
direction="row"
py={4}
alignContent="baseline"
width="100%"
justifyContent="center"
>
<RouterLink
href={{
pathname: `/app/${appScope}/${id}`,
query: router.query,
}}
passHref
>
<Button
as={Link}
variant="solid"
colorScheme="primary"
leftIcon={<IoIosJournal />}
>
Back to the journal
</Button>
</RouterLink>
{currentUserPermissions.includes("journals.update") && (
<Button
variant="solid"
colorScheme="primary"
disabled={showNewHolderForm}
hidden={!currentUserPermissions.includes("journals.update")}
onClick={() => toggleNewHolder(true)}
leftIcon={<TiGroupOutline />}
>
Add a team
</Button>
)}
{currentUserPermissions.includes("journals.delete") && (
<Button
variant="solid"
colorScheme="unsafe"
hidden={!currentUserPermissions.includes("journals.delete")}
onClick={() => setDeleteAlert(true)}
leftIcon={<DeleteIcon />}
>
Delete this journal
</Button>
)}
</Flex>
</VStack>
);
};
export default PermissionTable;

Wyświetl plik

@ -0,0 +1,45 @@
import React from "react";
import { useRadio, Box, Flex } from "@chakra-ui/react";
const RadioCard = (props) => {
const { getInputProps, getCheckboxProps } = useRadio(props);
const input = getInputProps();
const checkbox = getCheckboxProps();
return (
<Flex as="label" h="fill-availible" onClick={() => console.log('hello2')}>
<input {...input} />
<Box
justifyContent="left"
alignContent="center"
{...checkbox}
cursor="pointer"
borderWidth="1px"
borderRadius="md"
boxShadow="md"
_disabled={{
bg: "gray.300",
color: "gray.900",
borderColor: "gray.300",
}}
_checked={{
bg: "secondary.900",
color: "white",
borderColor: "secondary.900",
}}
_focus={{
boxShadow: "outline",
}}
px={5}
py={3}
fontWeight="600"
>
{props.children}
</Box>
</Flex>
);
};
// const RadioCard = chakra(RadioCard_);
export default RadioCard;

Wyświetl plik

@ -1,4 +1,3 @@
import { jsx } from "@emotion/react";
import {
useState,
@ -69,11 +68,9 @@ const SearchBar = (props) => {
delete newQuery.entryId;
ui.setSearchTerm(InputFieldValue);
router.push(
{ pathname: "/app/[appScope]/[id]/entries", query: newQuery },
undefined,
{ shallow: false }
);
router.push({ pathname: "/stream/", query: newQuery }, undefined, {
shallow: false,
});
}
} else {
setShowError(true);
@ -86,7 +83,9 @@ const SearchBar = (props) => {
useEffect(() => {
const cache =
router.params.appScope === "public" ? publicJournalsCache : journalsCache;
router.params.appScope === "personal"
? publicJournalsCache
: journalsCache;
if (router.params.id && !cache.isLoading) {
const newJournal = cache.data.find(
(journal) => journal.id === router.params.id

Wyświetl plik

@ -1,22 +1,20 @@
import {
ProSidebar,
Menu,
MenuItem,
import { jsx } from "@emotion/react";
import { ProSidebar } from "react-pro-sidebar";
import AppSidebar from "./AppSidebar";
SidebarHeader,
SidebarFooter,
SidebarContent,
} from "react-pro-sidebar";
import { useContext } from "react";
import RouterLink from "next/link";
import {
Button,
Image,
Menu,
MenuButton,
MenuList,
MenuItem,
MenuGroup,
MenuDivider,
Link,
} from "@chakra-ui/react";
import { ChevronDownIcon } from "@chakra-ui/icons";
import { Flex, Image, IconButton } from "@chakra-ui/react";
import UIContext from "../core/providers/UIProvider/context";
import React from "react";
import { HamburgerIcon } from "@chakra-ui/icons";
import { MdTimeline, MdSettings } from "react-icons/md";
// import RouterLink from "next/link";
// import RouterLink from "next/link";
const Sidebar = () => {
@ -29,8 +27,20 @@ const Sidebar = () => {
collapsed={ui.sidebarCollapsed}
hidden={!ui.sidebarVisible}
>
{!ui.isMobileView && (
<Link href="/" bgColor="primary.1200">
<SidebarHeader>
<Flex>
<IconButton
ml={4}
justifySelf="flex-start"
colorScheme="primary"
aria-label="App navigation"
icon={<HamburgerIcon />}
onClick={() => {
ui.isMobileView
? ui.setSidebarToggled(!ui.sidebarToggled)
: ui.setSidebarCollapsed(!ui.sidebarCollapsed);
}}
/>
<Image
// as={Link}
// to="/"
@ -40,64 +50,27 @@ const Sidebar = () => {
src="/icons/bugout-dev-white.svg"
alt="bugout.dev"
/>
</Link>
)}
{ui.isMobileView && (
<Menu>
<MenuButton
as={Button}
m={0}
variant="solid"
w={["100%", "100%", "18rem", "20rem", "22rem", "24rem"]}
p={5}
colorScheme="primary"
h="3rem"
borderRadius={0}
// bgColor="primary.900"
rightIcon={<ChevronDownIcon boxSize="1.5rem" />}
>
</Flex>
</SidebarHeader>
<SidebarContent>
<Menu iconShape="square">
<MenuItem icon={<MdTimeline />}>
{" "}
<Image
h="3rem"
py="0.75rem"
pl={5}
src="/icons/bugout-dev-white.svg"
alt="bugout.dev"
/>
</MenuButton>
<MenuList
zIndex="dropdown"
width={["100vw", "100vw", "18rem", "20rem", "22rem", "24rem"]}
borderRadius={0}
m={0}
>
<MenuGroup>
<RouterLink href="/case-studies/activeloop" passHref>
<MenuItem>case studies</MenuItem>
</RouterLink>
</MenuGroup>
<MenuDivider />
<RouterLink href="/events" passHref>
<MenuItem>events</MenuItem>
</RouterLink>
<RouterLink href="/team" passHref>
<MenuItem>team</MenuItem>
</RouterLink>
<RouterLink href="/pricing" passHref>
<MenuItem>pricing</MenuItem>
</RouterLink>
</MenuList>
<RouterLink href="/stream">Stream</RouterLink>
</MenuItem>
</Menu>
)}
{ui.isAppView && ui.isAppReady && ui.isLoggedIn && <AppSidebar />}
{/* <Menu iconShape="square">
<MenuItem>Dashboard</MenuItem>
<SubMenu title="Components">
<MenuItem>Component 1</MenuItem>
<MenuItem>Component 2</MenuItem>
</SubMenu>
</Menu> */}
<Menu iconShape="square">
<MenuItem icon={<MdSettings />}>
{" "}
<RouterLink href="/subscriptions">Subscriptions </RouterLink>
</MenuItem>
</Menu>
</SidebarContent>
<SidebarFooter>
{/**
* You can add a footer for the sidebar ex: copyright
*/}
</SidebarFooter>
</ProSidebar>
);
};

Wyświetl plik

@ -0,0 +1,111 @@
import React from "react";
import { Skeleton, IconButton } from "@chakra-ui/react";
import {
Table,
Th,
Td,
Tr,
Thead,
Tbody,
Editable,
EditableInput,
Image,
EditablePreview,
} from "@chakra-ui/react";
import { DeleteIcon } from "@chakra-ui/icons";
import moment from "moment";
import CopyButton from "./CopyButton";
import { useSubscriptions } from "../core/hooks";
import ConfirmationRequest from "./ConfirmationRequest";
const List = () => {
const { subscriptionsCache, changeNote, deleteSubscription } =
useSubscriptions();
const updateCallback = ({ id, note }) => {
changeNote.mutate({ id, note });
};
if (subscriptionsCache.data) {
return (
<Table
borderColor="gray.200"
borderWidth="1px"
variant="simple"
colorScheme="primary"
justifyContent="center"
borderBottomRadius="xl"
alignItems="baseline"
h="auto"
size="sm"
mt={0}
>
<Thead>
<Tr>
<Th>Token</Th>
<Th>Address</Th>
<Th>Date Created</Th>
<Th>Note</Th>
<Th>Actions</Th>
</Tr>
</Thead>
<Tbody>
{subscriptionsCache.data.subscriptions.map((subscription) => {
return (
<Tr key={`token-row-${subscription.address}`}>
<Td>
<Image
h="32px"
src="https://ethereum.org/static/c48a5f760c34dfadcf05a208dab137cc/31987/eth-diamond-rainbow.png"
/>
</Td>
<Td mr={4} p={0}>
<CopyButton>{subscription.address}</CopyButton>
</Td>
<Td py={0}>{moment(subscription.created_at).format("L")}</Td>
<Td py={0}>
<Editable
colorScheme="primary"
placeholder="enter note here"
defaultValue={subscription.note}
onSubmit={(nextValue) =>
updateCallback({
id: subscription.id,
note: nextValue,
})
}
>
<EditablePreview
maxW="40rem"
_placeholder={{ color: "black" }}
/>
<EditableInput maxW="40rem" />
</Editable>
</Td>
<Td py={0}>
<ConfirmationRequest
bodyMessage={"please confirm"}
header={"Delete subscription"}
onConfirm={() => deleteSubscription.mutate(subscription.id)}
>
<IconButton
size="sm"
variant="ghost"
colorScheme="primary"
icon={<DeleteIcon />}
/>
</ConfirmationRequest>
</Td>
</Tr>
);
})}
</Tbody>
</Table>
);
} else if (subscriptionsCache.isLoading) {
return <Skeleton />;
} else {
return "";
}
};
export default List;

Wyświetl plik

@ -0,0 +1,31 @@
import React from "react";
import { Tag, TagLabel, Flex } from "@chakra-ui/react";
const Tags = ({ tags }) => {
const displayTags = tags?.filter(
(tag) =>
tag.startsWith("from") ||
tag.startsWith("client") ||
tag.startsWith("network") ||
tag.startsWith("to") ||
tag.startsWith("source") ||
tag.startsWith("node")
);
return (
<Flex alignSelf="flex-start">
<Flex flexWrap="wrap" pl={2} pr={2} spacing={2} alignItems="center">
{displayTags?.map((tag, index) => (
<Tag
variant="subtle"
colorScheme="primary"
key={`${tag}-${index}`}
zIndex={1}
>
<TagLabel>{tag}</TagLabel>
</Tag>
))}
</Flex>
</Flex>
);
};
export default Tags;

Wyświetl plik

@ -1,82 +0,0 @@
import { jsx } from "@emotion/react";
import { useCallback, useState, useEffect } from "react";
import { Tag, TagLabel, TagCloseButton, Flex, Input } from "@chakra-ui/react";
import { useRouter, useUpdateTag } from "../core/hooks";
import { useQueryCache } from "react-query";
const TagsEditor = ({ entry }) => {
const router = useRouter();
const [canEditTags, setEditTags] = useState(false);
const cache = useQueryCache();
const { id: journalId, entryId, appScope } = router.params;
const [tag, setTag] = useState("");
const updateTag = useUpdateTag(journalId, entryId);
const onTagAdd = useCallback(
(e) => {
if (e.keyCode !== 13) {
return;
}
if (!entry.tags.includes(e.target.value)) {
updateTag({ tag: e.target.value, action: "add" });
}
setTag("");
},
[entry, setTag, updateTag]
);
useEffect(() => {
const userPermissions = cache.getQueryData([
"journal-permissions-current-user",
{ journalId },
]);
if (userPermissions && appScope !== "public") {
if (userPermissions.includes("journals.entries.update")) {
setEditTags(true);
} else {
setEditTags(false);
}
} else {
setEditTags(false);
}
}, [cache, appScope, journalId]);
const onTagDelete = useCallback(
(index) => {
updateTag({ tag: entry.tags[index], action: "delete" });
},
[entry, updateTag]
);
return (
<Flex alignSelf="flex-start">
<Flex flexWrap="wrap" pl={2} pr={2} spacing={2} alignItems="center">
{entry?.tags?.map((tag, index) => (
<Tag
variant="subtle"
colorScheme="primary"
// size="sm"
key={`${tag}-${index}`}
zIndex={1}
>
<TagLabel>{tag}</TagLabel>
{canEditTags && (
<TagCloseButton onClick={() => onTagDelete(index)} />
)}
</Tag>
))}
{canEditTags && (
<Input
variant="newTag"
placeholder="+ Add tag"
onKeyUp={onTagAdd}
value={tag}
onChange={(e) => setTag(e.target.value)}
/>
)}
</Flex>
</Flex>
);
};
export default TagsEditor;

Wyświetl plik

@ -1,5 +1,4 @@
import { jsx } from "@emotion/react";
import React from "react";
import { IconButton } from "@chakra-ui/react";
import {
Table,
@ -13,7 +12,9 @@ import {
Spinner,
} from "@chakra-ui/react";
import { DeleteIcon } from "@chakra-ui/icons";
import { CopyButton, ConfirmationRequest, NewTokenTr } from ".";
import CopyButton from "./CopyEntryButton";
import ConfirmationRequest from "./ConfirmationRequest";
import NewTokenTr from "./NewTokenTr";
import { useForm } from "react-hook-form";
const TokenList = ({

Wyświetl plik

@ -1,89 +0,0 @@
import { jsx } from "@emotion/react";
import { Skeleton, IconButton } from "@chakra-ui/react";
import {
Table,
Th,
Td,
Tr,
Thead,
Tbody,
Editable,
EditableInput,
EditablePreview,
} from "@chakra-ui/react";
import { DeleteIcon } from "@chakra-ui/icons";
import moment from "moment";
import { CopyButton } from ".";
const List = ({ data, revoke, isLoading, updateCallback }) => {
const userToken = localStorage.getItem("BUGOUT_ACCESS_TOKEN");
if (data) {
return (
<Table
variant="simple"
colorScheme="primary"
justifyContent="center"
alignItems="baseline"
h="auto"
size="sm"
>
<Thead>
<Tr>
<Th>Token</Th>
<Th>Date Created</Th>
<Th>Note</Th>
<Th>Actions</Th>
</Tr>
</Thead>
<Tbody>
{data.data.token.map((token) => {
if (token.active) {
if (userToken !== token.id) {
return (
<Tr key={`token-row-${token.id}`}>
<Td mr={4} p={0}>
<CopyButton>{token.id}</CopyButton>
</Td>
<Td py={0}>{moment(token.created_at).format("L")}</Td>
<Td py={0}>
<Editable
colorScheme="primary"
placeholder="enter note here"
defaultValue={token.note}
onSubmit={(nextValue) =>
updateCallback({ token: token.id, note: nextValue })
}
>
<EditablePreview
maxW="40rem"
_placeholder={{ color: "black" }}
/>
<EditableInput maxW="40rem" />
</Editable>
</Td>
<Td py={0}>
<IconButton
size="sm"
variant="ghost"
colorScheme="primary"
onClick={() => revoke(token.id)}
icon={<DeleteIcon />}
/>
</Td>
</Tr>
);
} else return null;
} else return null;
})}
</Tbody>
</Table>
);
} else if (isLoading) {
return <Skeleton />;
} else {
return "";
}
};
export default List;

Wyświetl plik

@ -5,17 +5,18 @@ import { AuthService } from "../../core/services";
const useChangePassword = () => {
const toast = useToast();
const [changePassword, { isLoading, data }] = useMutation(
AuthService.changePassword,
{
onError: (error) => {
toast(error, "error");
},
onSuccess: () => {
toast("Your password has been successfully changed", "success");
},
}
);
const {
mutate: changePassword,
isLoading,
data,
} = useMutation(AuthService.changePassword, {
onError: (error) => {
toast(error, "error");
},
onSuccess: () => {
toast("Your password has been successfully changed", "success");
},
});
return { changePassword, isLoading, data };
};

Wyświetl plik

@ -5,7 +5,12 @@ import { useToast } from ".";
const useEntriesSearch = ({ journalId }) => {
const toast = useToast();
const [entriesSearch, { isLoading, error, data }] = useMutation(
const {
mutateAsync: entriesSearch,
isLoading,
error,
data,
} = useMutation(
JournalService.searchEntries({ journalId }),
{

Wyświetl plik

@ -15,39 +15,42 @@ const useJournalEntries = ({
journalId,
});
const getEntries = (searchTerm) => async (key, jorunalId, nextPage = 0) => {
if (!nextPage) {
nextPage = 0;
}
const searchTags = searchTerm.split(" ").filter(function (n) {
if (n.startsWith("#")) return n;
else {
return null;
const getEntries =
(searchTerm) =>
async ({ pageParam = 0 }) => {
if (!pageParam) {
pageParam = 0;
}
});
const data = await entriesSearch({
searchTerm,
journalType,
isContent,
limit,
offset: nextPage,
searchTags,
});
const searchTags = searchTerm.split(" ").filter(function (n) {
if (n.startsWith("#")) return n;
else {
return null;
}
});
const newEntryList = data.data.results.map((entry) => ({
...entry,
id: entry.entry_url.split("/").pop(),
}));
return {
data: [...newEntryList],
nextPage: nextPage + 1,
next_offset: data.data.next_offset,
total_results: data.data.total_results,
offset: data.data.offset,
const data = await entriesSearch({
searchTerm,
journalType,
isContent,
limit,
offset: pageParam,
searchTags,
});
const newEntryList = data.data.results.map((entry) => ({
...entry,
id: entry.entry_url.split("/").pop(),
}));
return {
data: [...newEntryList],
pageParams: {
pageParam: pageParam + 1,
next_offset: data.data.next_offset,
total_results: data.data.total_results,
offset: data.data.offset,
},
};
};
};
const {
data: EntriesPages,
@ -60,9 +63,10 @@ const useJournalEntries = ({
["journal-entries", { journalId }],
getEntries(searchQuery),
{
refetchInterval: 1000,
...queryCacheProps,
getNextPageParam: (lastPage) => lastPage.next_offset ?? false,
getFetchMore: (lastGroup) => {
// getNextPageParam: (lastPage) => lastPage.next_offset ?? false,
getNextPageParam: (lastGroup) => {
return lastGroup.next_offset === null ? false : lastGroup.next_offset;
},
enabled: !!journalId,

Wyświetl plik

@ -16,17 +16,11 @@ const useJournalEntry = (journalId, entryId, journalScope) => {
return entry;
};
const {
data,
isLoading,
isFetchedAfterMount,
refetch,
isError,
error,
} = useQuery(["journal-entry", { journalId, entryId }], getEntry, {
...queryCacheProps,
onError: (error) => toast(error, "error"),
});
const { data, isLoading, isFetchedAfterMount, refetch, isError, error } =
useQuery(["journal-entry", { journalId, entryId }], getEntry, {
...queryCacheProps,
onError: (error) => toast(error, "error"),
});
return { data, isFetchedAfterMount, isLoading, refetch, isError, error };
};

Wyświetl plik

@ -1,12 +1,17 @@
import { useQuery, useMutation, useQueryCache } from "react-query";
import { useQuery, useMutation, useQueryClient } from "react-query";
import { JournalService } from "../services";
import { useToast } from ".";
import { queryCacheProps } from "./hookCommon";
const useJournalPermissions = (journalId, journalScope) => {
const cache = useQueryCache();
const cache = useQueryClient();
const toast = useToast();
const { data, isLoading, refetch: getPermissions, error } = useQuery(
const {
data,
isLoading,
refetch: getPermissions,
error,
} = useQuery(
["journal-permissions", { journalId }],
async () => {
if (journalId) {
@ -64,7 +69,7 @@ const useJournalPermissions = (journalId, journalScope) => {
}
);
const [setJournalPermission, setJournalPermissionStatus] = useMutation(
const setJournalPermissionMutation = useMutation(
JournalService.setJournalPermission(journalId),
{
onMutate: (data) => {
@ -111,7 +116,7 @@ const useJournalPermissions = (journalId, journalScope) => {
}
);
const [removeJournalPermission, removeJournalPermissionStatus] = useMutation(
const removeJournalPermissionMutation = useMutation(
JournalService.deleteJournalPermission(journalId),
{
onMutate: (data) => {
@ -125,11 +130,10 @@ const useJournalPermissions = (journalId, journalScope) => {
const index = previousJournalPermissionResponse.findIndex(
(i) => i.holder_id === data.holder_id
);
newJournalPermissionResponse[
index
].permissions = newJournalPermissionResponse[index].permissions.filter(
(value) => !data.permission_list.includes(value)
);
newJournalPermissionResponse[index].permissions =
newJournalPermissionResponse[index].permissions.filter(
(value) => !data.permission_list.includes(value)
);
if (newJournalPermissionResponse[index].permissions.length < 1) {
newJournalPermissionResponse.splice(index, 1);
@ -154,17 +158,9 @@ const useJournalPermissions = (journalId, journalScope) => {
}
);
//ToDo: const addUserMutation = useMutation(.. when upgrading to React Query 3
const setJournalPermissionMutation = {
setJournalPermission,
setJournalPermissionStatus,
};
//ToDo: const removeJournalPermissionMutation = useMutation(.. when upgrading to React Query 3
const removeJournalPermissionMutation = {
removeJournalPermission,
removeJournalPermissionStatus,
};
const holders = data;
return {

Wyświetl plik

@ -1,5 +1,5 @@
import { useEffect, useCallback, useContext } from "react";
import { useMutation, useQueryCache } from "react-query";
import { useMutation, useQueryClient } from "react-query";
import { useUser, useRouter, useAnalytics } from ".";
import UIContext from "../providers/UIProvider/context";
import { AuthService } from "../services";
@ -8,7 +8,7 @@ const useLogout = () => {
const { setLoggingOut } = useContext(UIContext);
const router = useRouter();
const analytics = useAnalytics();
const [revoke, { data }] = useMutation(AuthService.revoke, {
const {mutate: revoke, data } = useMutation(AuthService.revoke, {
onSuccess: () => {
if (analytics.isLoaded) {
analytics.mixpanel.track(
@ -19,7 +19,7 @@ const useLogout = () => {
},
});
const { setUser } = useUser();
const cache = useQueryCache();
const cache = useQueryClient();
const logout = useCallback(() => {
setLoggingOut(true);

Wyświetl plik

@ -43,7 +43,7 @@ const useSignUp = (source) => {
const requested_pricing = window.sessionStorage.getItem(
"requested_pricing_plan"
);
const redirectURL = requested_pricing ? "/account/teams" : "/app";
const redirectURL = requested_pricing ? "/subscriptions" : "/stream";
router.push(redirectURL);
window.sessionStorage.clear("requested_pricing");

Wyświetl plik

@ -5,19 +5,58 @@ import { queryCacheProps } from "./hookCommon";
import useStripe from "./useStripe";
import { useQuery } from "react-query";
const useSubscriptions = (groupId) => {
const useSubscriptions = () => {
const toast = useToast();
const stripe = useStripe();
const [manageSubscription, mangeSeatsStatus] = useMutation(
SubscriptionsService.manageSubscription(),
// const manageSubscription = useMutation(
// SubscriptionsService.manageSubscription(),
// {
// onError: (error) => toast(error, "error"),
// onSuccess: (response) => {
// const { session_id: sessionId, session_url: sessionUrl } =
// response.data;
// if (sessionId) {
// stripe.redirectToCheckout({ sessionId });
// } else if (sessionUrl) {
// window.location = sessionUrl;
// }
// },
// }
// );
const getSubscriptions = async () => {
const response = await SubscriptionsService.getSubscriptions();
return response.data.data;
};
const subscriptionsCache = useQuery(["subscriptions"], getSubscriptions, {
...queryCacheProps,
onError: (error) => {
toast(error, "error");
},
});
const getSubscriptionTypes = async () => {
const response = await SubscriptionsService.getTypes();
return response.data.data;
};
const typesCache = useQuery(["subscription_types"], getSubscriptionTypes, {
...queryCacheProps,
onError: (error) => {
toast(error, "error");
},
});
const createSubscription = useMutation(
SubscriptionsService.createSubscription(),
{
onError: (error) => toast(error, "error"),
onSuccess: (response) => {
const {
session_id: sessionId,
session_url: sessionUrl,
} = response.data;
subscriptionsCache.refetch();
const { session_id: sessionId, session_url: sessionUrl } =
response.data;
if (sessionId) {
stripe.redirectToCheckout({ sessionId });
} else if (sessionUrl) {
@ -27,28 +66,21 @@ const useSubscriptions = (groupId) => {
}
);
const manageSubscriptionMutation = {
manageSubscription,
isLoading: mangeSeatsStatus.isLoading,
};
const changeNote = useMutation(SubscriptionsService.modifySubscription(), {
onError: (error) => toast(error, "error"),
onSuccess: (response) => {
subscriptionsCache.refetch();
},
});
const getSubscriptions = async () => {
const response = await SubscriptionsService.getSubscriptions(groupId);
return response.data.subscriptions;
};
const deleteSubscription = useMutation(SubscriptionsService.deleteSubscription(), {
onError: (error) => toast(error, "error"),
onSuccess: (response) => {
subscriptionsCache.refetch();
},
});
const subscriptionsCache = useQuery(
["subscriptions", groupId],
getSubscriptions,
{
...queryCacheProps,
onError: (error) => {
toast(error, "error");
},
}
);
return { manageSubscriptionMutation, subscriptionsCache };
return { createSubscription, subscriptionsCache, typesCache, changeNote, deleteSubscription };
};
export default useSubscriptions;

Wyświetl plik

@ -2,16 +2,19 @@ import { useMutation } from "react-query";
import { AuthService } from "../services";
const useTokens = () => {
const [list, { isLoading, error, data }] = useMutation(
AuthService.getTokenList
);
const [revoke] = useMutation(AuthService.revokeToken, {
const {
mutate: list,
isLoading,
error,
data,
} = useMutation(AuthService.getTokenList);
const { mutate: revoke } = useMutation(AuthService.revokeToken, {
onSuccess: () => {
list();
},
});
const [update] = useMutation(AuthService.updateToken, {
const { mutate: update } = useMutation(AuthService.updateToken, {
onSuccess: () => {
list();
},

Wyświetl plik

@ -1,10 +1,10 @@
import { useMutation, useQueryCache } from "react-query";
import { useMutation, useQueryClient } from "react-query";
import { EntryService } from "../services";
import { useToast } from ".";
const useUpdateEntry = (journalId, entryId) => {
const entriesCache = useQueryCache();
const entryCache = useQueryCache();
const entriesCache = useQueryClient();
const entryCache = useQueryClient();
const toast = useToast();
const handleError = (error, variables, context) => {
@ -20,49 +20,52 @@ const useUpdateEntry = (journalId, entryId) => {
toast(error, "error");
};
const [updateEntry] = useMutation(EntryService.update(journalId, entryId), {
onMutate: (newData) => {
const prevEntriesPages = entriesCache.getQueryData([
"journal-entries",
{ journalId },
]);
const { mutate: updateEntry } = useMutation(
EntryService.update(journalId, entryId),
{
onMutate: (newData) => {
const prevEntriesPages = entriesCache.getQueryData([
"journal-entries",
{ journalId },
]);
const newEntriesPages = JSON.parse(JSON.stringify(prevEntriesPages));
const prevEntry = entryCache.getQueryData([
"journal-entry",
{ journalId, entryId },
]);
const newEntriesPages = JSON.parse(JSON.stringify(prevEntriesPages));
const prevEntry = entryCache.getQueryData([
"journal-entry",
{ journalId, entryId },
]);
const newEntry = { ...prevEntry, ...newData };
const newEntry = { ...prevEntry, ...newData };
newEntriesPages.map((page) => {
page.data = page.data.map((entry) => {
if (entry.id === entryId) {
return {
...entry,
...newData,
// for tags useUpdateTag instead
};
}
return entry;
newEntriesPages.map((page) => {
page.data = page.data.map((entry) => {
if (entry.id === entryId) {
return {
...entry,
...newData,
// for tags useUpdateTag instead
};
}
return entry;
});
return page;
});
return page;
});
entriesCache.setQueryData(
["journal-entries", { journalId }],
newEntriesPages
);
entryCache.setQueryData(
["journal-entry", { journalId, entryId }],
newEntry
);
entriesCache.setQueryData(
["journal-entries", { journalId }],
newEntriesPages
);
entryCache.setQueryData(
["journal-entry", { journalId, entryId }],
newEntry
);
return { prevEntriesPages, prevEntry };
},
onError: (error, variables, context) =>
handleError(error, variables, context),
});
return { prevEntriesPages, prevEntry };
},
onError: (error, variables, context) =>
handleError(error, variables, context),
}
);
return updateEntry;
};

Wyświetl plik

@ -19,6 +19,9 @@ const UIProvider = ({ children }) => {
const { modal, toggleModal } = useContext(ModalContext);
const [searchTerm, setSearchTerm] = useQuery("q", " ", true, false);
const [entryId, setEntryId] = useState();
const [searchBarActive, setSearchBarActive] = useState(false);
// ******* APP state ********
@ -26,8 +29,9 @@ const UIProvider = ({ children }) => {
const [isLoggingOut, setLoggingOut] = useState(false);
const [isAppReady, setAppReady] = useState(false);
const [isAppView, setAppView] = useState(
router.nextRouter.asPath.includes("/app") ||
router.nextRouter.asPath.includes("/account")
router.nextRouter.asPath.includes("/stream") ||
router.nextRouter.asPath.includes("/account") ||
router.nextRouter.asPath.includes("/subscriptions")
);
useEffect(() => {
@ -61,8 +65,9 @@ const UIProvider = ({ children }) => {
useEffect(() => {
setAppView(
router.nextRouter.asPath.includes("/app") ||
router.nextRouter.asPath.includes("/account")
router.nextRouter.asPath.includes("/stream") ||
router.nextRouter.asPath.includes("/account") ||
router.nextRouter.asPath.includes("/subscriptions")
);
}, [router.nextRouter.asPath, user]);
@ -72,13 +77,13 @@ const UIProvider = ({ children }) => {
const [sidebarVisible, setSidebarVisible] = useStorage(
window.sessionStorage,
"sidebarVisible",
false
true
);
// Whether sidebar should be smaller state
const [sidebarCollapsed, setSidebarCollapsed] = useStorage(
window.sessionStorage,
"sidebarCollapsed",
false
true
);
// Whether sidebar should be toggled in mobile view
@ -96,14 +101,6 @@ const UIProvider = ({ children }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isMobileView]);
// //When entrering appView - start with showing sidebar at all times
useEffect(() => {
if (isAppView) {
setSidebarVisible(true);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isAppView]);
// *********** Entries layout states **********************
/**
@ -111,18 +108,11 @@ const UIProvider = ({ children }) => {
* Default true in mobile mode and false in desktop mode
*/
const [entriesViewMode, setEntriesViewMode] = useState(
isMobileView ? (router.params?.entryId ? "entry" : "list") : "split"
router.params?.entryId ? "entry" : "list"
);
useEffect(() => {
setEntriesViewMode(
isMobileView ? (router.params?.entryId ? "entry" : "list") : "split"
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isMobileView]);
useEffect(() => {
setEntriesViewMode(isMobileView ? "list" : "split");
setEntriesViewMode(router.params?.entryId ? "entry" : "list");
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [router.params?.id]);
@ -151,6 +141,8 @@ const UIProvider = ({ children }) => {
setEntriesViewMode,
modal,
toggleModal,
entryId,
setEntryId,
}}
>
{children}

Wyświetl plik

@ -3,6 +3,7 @@ import { http } from "../utils";
const AUTH_URL = process.env.NEXT_PUBLIC_SIMIOTICS_AUTH_URL;
export const login = ({ username, password }) => {
console.log('login',username, password)
const data = new FormData();
data.append("username", username);
data.append("password", password);

Wyświetl plik

@ -1,26 +1,78 @@
import { http } from "../utils";
// import axios from "axios";
const AUTH_URL = process.env.NEXT_PUBLIC_SIMIOTICS_AUTH_URL;
const API = process.env.NEXT_PUBLIC_SIMIOTICS_AUTH_URL;
export const manageSubscription = () => ({
groupId,
desiredUnits,
planType,
}) => {
export const getTypes = () =>
http({
method: "GET",
url: `${API}/subscription_types/`,
});
export const getSubscriptions = () =>
http({
method: "GET",
url: `${API}/subscriptions/`,
});
export const create = ({ address, note, blockchain }) => {
const data = new FormData();
data.append("group_id", groupId);
data.append("units_required", desiredUnits);
data.append("plan_type", planType);
return http({
data.append("address", address);
data.append("note", note);
data.append("blockchain", blockchain);
http({
method: "POST",
url: `${AUTH_URL}/subscription/manage`,
url: `${API}/subscriptions/`,
data,
});
};
export const getSubscriptions = (groupId) => {
export const deleteJournal = (id) => () =>
http({
method: "DELETE",
url: `${API}/journals/${id}`,
});
export const createSubscription =
() =>
({ address, type, note }) => {
console.log("createSubscription: ", address, type, note);
const data = new FormData();
data.append("address", address);
data.append("subscription_type", type);
data.append("note", note);
return http({
method: "POST",
url: `${API}/subscriptions/`,
data,
});
};
export const modifySubscription =
() =>
({ id, note }) => {
console.log("modifySubscription: ", note, id);
const data = new FormData();
data.append("note", note);
data.append("id", id);
return http({
method: "POST",
url: `${API}/subscription/${id}`,
data,
});
};
export const deleteSubscription = () => (id) => {
console.log("deleteSubscription: ", id);
return http({
method: "GET",
url: `${AUTH_URL}/groups/${groupId}/subscriptions`,
method: "DELETE",
url: `${API}/subscription/${id}`,
});
};
// export const getSubscriptions = (groupId) => {
// return http({
// method: "GET",
// url: `${API}/groups/${groupId}/subscriptions`,
// });
// };

Wyświetl plik

@ -1,4 +1,7 @@
import axios from "axios";
import enableMockupRequests from "./mockupRequests";
let axios = require("axios");
enableMockupRequests(axios);
const http = (config) => {
const token = localStorage.getItem("BUGOUT_ACCESS_TOKEN");
@ -15,4 +18,5 @@ const http = (config) => {
return axios(options);
};
export { axios };
export default http;

Wyświetl plik

@ -0,0 +1,115 @@
import moment from "moment";
const MOCK_API = process.env.NEXT_PUBLIC_SIMIOTICS_AUTH_URL;
var MockAdapter = require("axios-mock-adapter");
const makeid = (length) => {
var result = "";
var characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
};
const randDate = () => {
return moment(
new Date(+new Date() - Math.floor(Math.random() * 10000000000))
).format("MM/DD/YYYY");
};
let MockSubscriptions = [
{
address: makeid(24),
id: makeid(24),
notes: "lorem", //ToDo: rename in to label
created_at: randDate(),
subscription_type: "ethereum_blockchain",
},
{
address: makeid(24),
id: makeid(24),
notes: "lorem",
created_at: randDate(),
subscription_type: "ethereum_txpool",
},
{
address: makeid(24),
id: makeid(24),
notes: "lorem",
created_at: randDate(),
subscription_type: "algorand_blockchain",
},
{
address: makeid(24),
id: makeid(24),
notes: "lorem",
created_at: randDate(),
subscription_type: "ethereum_blockchain",
},
{
address: makeid(24),
id: makeid(24),
notes: "lorem",
created_at: randDate(),
subscription_type: "ethereum_blockchain",
},
];
const enableMockupRequests = (axiosInstance) => {
let mock = new MockAdapter(axiosInstance, { onNoMatch: "passthrough" });
mock.onGet(`${MOCK_API}/subscription_types/`).reply(200, {
data: [
{
subscription_type: "ethereum_blockchain",
id: makeid(24),
name: "Ethereum",
active: true,
subscription_plan_id: makeid(24),
},
{
subscription_type: "ethereum_txpool",
id: makeid(24),
name: "Ethereum Transaction Pool",
active: true,
subscription_plan_id: makeid(24),
},
{
subscription_type: "algorand_blockchain",
id: makeid(24),
name: "Algorand",
active: false,
subscription_plan_id: makeid(24),
},
{
subscription_type: "free",
id: makeid(24),
name: "Free subscription",
active: true,
subscription_plan_id: null,
},
],
});
mock.onGet(`${MOCK_API}/subscriptions/`).reply(200, {
data: {
is_free_subscription_availible: true,
subscriptions: MockSubscriptions,
},
});
mock.onPost(`${MOCK_API}/subscriptions/`).reply((config) => {
const params = config.data; // FormData of {name: ..., file: ...}
const id = params.get("id");
const note = params.get("note");
return new Promise(function (resolve) {
setTimeout(function () {
const data = { id, note };
console.log("mock", id, note);
MockSubscriptions.push({ ...data });
resolve([200, { message: "OK", result: true }]);
}, 1000);
});
});
};
export default enableMockupRequests;

Wyświetl plik

@ -1,9 +1,8 @@
import { jsx } from "@emotion/react";
import React from "react";
import { useBreakpointValue, Flex } from "@chakra-ui/react";
import SplitPane, { Pane } from "react-split-pane";
import { getLayout as getSiteLayout } from "./AppLayout";
import { EntriesNavigation } from "../components";
import EntriesNavigation from "../components/EntriesNavigation";
import { useContext } from "react";
import UIContext from "../core/providers/UIProvider/context";
const EntriesLayout = (props) => {

Wyświetl plik

@ -65,13 +65,13 @@
z-index: 101;
}
.pro-sidebar > .pro-sidebar-inner > .pro-sidebar-layout .pro-sidebar-header {
border-bottom: 1px solid rgba(173, 173, 173, 0.2);
border-bottom: 1px solid #212990;
}
.pro-sidebar > .pro-sidebar-inner > .pro-sidebar-layout .pro-sidebar-content {
flex-grow: 1;
}
.pro-sidebar > .pro-sidebar-inner > .pro-sidebar-layout .pro-sidebar-footer {
border-top: 1px solid rgba(173, 173, 173, 0.2);
border-top: 1px solid #212990;
}
.pro-sidebar > .pro-sidebar-inner > .pro-sidebar-layout ul {
list-style-type: none;
@ -84,7 +84,7 @@
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.3);
background-color: white;
z-index: 100;
display: none;
}
@ -262,7 +262,7 @@
}
.pro-sidebar .pro-menu > ul > .pro-sub-menu > .pro-inner-list-item {
position: relative;
background-color: #2b2b2b;
background-color: #212990;
}
.pro-sidebar .pro-menu > ul > .pro-sub-menu > .pro-inner-list-item > div > ul {
padding-top: 15px;
@ -331,6 +331,7 @@
height: 100%;
align-items: center;
justify-content: center;
color: white
}
.pro-sidebar .pro-menu .pro-menu-item > .pro-inner-item > .pro-item-content {
flex-grow: 1;
@ -346,6 +347,7 @@
.pro-icon-wrapper
.pro-icon {
animation: swing ease-in-out 0.5s 1 alternate;
color: white
}
.pro-sidebar .pro-menu .pro-menu-item.pro-sub-menu > .pro-inner-item:before {
background: #adadad;
@ -420,7 +422,7 @@
.pro-menu-item
> .pro-inner-item
> .pro-icon-wrapper {
background-color: #2b2b2b;
background-color: #212990;
}
.pro-sidebar
.pro-menu.square
@ -429,6 +431,9 @@
> .pro-icon-wrapper {
border-radius: 0;
}
.pro-menu-item {
color: white;
}
.pro-sidebar
.pro-menu.round
.pro-menu-item
@ -462,7 +467,7 @@
opacity: 0;
}
.pro-sidebar.collapsed .pro-menu > ul > .pro-menu-item > .pro-inner-list-item {
background-color: #2b2b2b;
background-color: white;
z-index: 111;
}
.pro-sidebar.collapsed .pro-menu > ul > .pro-menu-item::before {

Wyświetl plik

@ -179,3 +179,8 @@
width: 100%;
padding: 0.5rem;
}
code {
white-space: pre-line !important;
}