@chakra-ui/react#AlertIcon JavaScript Examples

The following examples show how to use @chakra-ui/react#AlertIcon. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: components.js    From idena-web with MIT License 6 votes vote down vote up
export function CommunityTranslationUnavailable() {
  const {t} = useTranslation()
  return (
    <Box mt={4}>
      <Alert
        status="error"
        bg="red.010"
        borderWidth="1px"
        borderColor="red.050"
        fontWeight={500}
        rounded="md"
        px={3}
        py={2}
      >
        <AlertIcon name="info" color="red.500" size={5} mr={3} />
        {t('Community translation is not available')}
      </Alert>
    </Box>
  )
}
Example #2
Source File: components.js    From idena-web with MIT License 6 votes vote down vote up
export function SuccessAlert({icon, children, ...props}) {
  return (
    <Alert
      status="success"
      bg="green.010"
      borderWidth="1px"
      borderColor="green.050"
      fontWeight={500}
      rounded="md"
      px={3}
      py={2}
      {...props}
    >
      {icon || <AlertIcon color="green.500" boxSize={5} mr={3} />}
      {children}
    </Alert>
  )
}
Example #3
Source File: PageNotFound.js    From web-client with Apache License 2.0 6 votes vote down vote up
PageNotFound = () => {
    return <Alert
        status='error'
        variant='subtle'
        flexDirection='column'
        alignItems='center'
        justifyContent='center'
        textAlign='center'
    >
        <AlertIcon boxSize='40px' mr={0} />
        <AlertTitle mt={4} mb={1} fontSize='lg'>
            Page not found
        </AlertTitle>
        <AlertDescription>
            There is nothing at this address. Please navigate to another place.
        </AlertDescription>
    </Alert>
}
Example #4
Source File: containers.js    From idena-web with MIT License 5 votes vote down vote up
export function DnaSendSucceededDialog({hash, url, onCompleteSend, ...props}) {
  const {t} = useTranslation()
  return (
    <Dialog closeOnOverlayClick={false} closeOnEsc={false} {...props}>
      <DialogBody color="brandGray.500">
        <Stack spacing={5}>
          <Alert
            status="success"
            bg="green.010"
            borderRadius="lg"
            flexDirection="column"
            justifyContent="center"
            height={132}
          >
            <Stack spacing={2} align="center">
              <AlertIcon size={8} mr={0} />
              <AlertTitle fontSize="lg" fontWeight={500}>
                {t('Successfully sent')}
              </AlertTitle>
            </Stack>
          </Alert>
          <Stack spacing={1}>
            <Stack spacing={1} py={2}>
              <Box color="muted">{t('Tx hash')}</Box>
              <Box wordBreak="break-all" fontWeight={500}>
                {hash}
              </Box>
            </Stack>
            <ExternalLink href={`https://scan.idena.io/transaction/${hash}`}>
              {t('Open in blockchain explorer')}
            </ExternalLink>
          </Stack>
        </Stack>
      </DialogBody>
      <DialogFooter>
        {url ? (
          <PrimaryButton
            onClick={() => {
              openExternalUrl(url)
              onCompleteSend()
            }}
          >
            {t('Continue')}
          </PrimaryButton>
        ) : (
          <PrimaryButton onClick={onCompleteSend}>{t('Close')}</PrimaryButton>
        )}
      </DialogFooter>
    </Dialog>
  )
}
Example #5
Source File: components.js    From idena-web with MIT License 5 votes vote down vote up
export function Toast({
  title,
  description,
  icon = 'info',
  status = 'info',
  actionContent,
  actionColor = status === 'error' ? 'red.500' : 'brandBlue.500',
  color,
  onAction,
  duration,
  ...props
}) {
  return (
    <Alert
      status={status}
      bg="white"
      boxShadow="0 3px 12px 0 rgba(83, 86, 92, 0.1), 0 2px 3px 0 rgba(83, 86, 92, 0.2)"
      color={color || 'brandGray.500'}
      fontSize="md"
      pl={4}
      pr={actionContent ? 2 : 5}
      pt="10px"
      pb={3}
      mb={5}
      minH="44px"
      rounded="lg"
      {...props}
    >
      <AlertIcon
        name={icon}
        size={5}
        color={color || (status === 'error' ? 'red.500' : 'blue.500')}
      />
      <Flex direction="column" align="flex-start" maxW={['90vw', 'sm']}>
        <AlertTitle fontWeight={500} lineHeight="base">
          {title}
        </AlertTitle>
        <AlertDescription
          color={color || 'muted'}
          lineHeight="base"
          textAlign="left"
          maxW={['77vw', 'none']}
          w="full"
          isTruncated
        >
          {description}
        </AlertDescription>
      </Flex>
      {actionContent && (
        <Button
          variant="ghost"
          color={actionColor}
          fontWeight={500}
          lineHeight="base"
          px={3}
          py="3/2"
          _hover={{bg: 'unset'}}
          _active={{bg: 'unset'}}
          _focus={{boxShadow: 'none'}}
          onClick={onAction}
        >
          {actionContent}
        </Button>
      )}
      <Box
        bg="gray.100"
        height="3px"
        roundedBottom={2}
        pos="absolute"
        bottom={0}
        left={0}
        right={0}
        animation={`${escape} ${duration}ms linear forwards`}
      />
    </Alert>
  )
}
Example #6
Source File: Results.js    From web-client with Apache License 2.0 5 votes vote down vote up
SearchResults = React.memo(() => {
    const params = useParams();
    const query = useQuery();
    const keywords = decodeURIComponent(params.keywords);

    const entitiesParam = query.has('entities') ? query.get('entities') : 'commands,tasks,vulnerabilities,vulnerability_templates,projects,project_templates';
    const entities = useMemo(() => entitiesParam.split(','), [entitiesParam]);

    const [emptyResults, setEmptyResults] = useState([]);

    return <>
        <PageTitle value={`${keywords} search results`} />
        <div className='heading'>
            <Breadcrumb />
            <div>
                <LinkButton href="/advanced-search">Advanced search</LinkButton>
            </div>
        </div>
        <Title type='Search results' title={`For ${keywords}`} icon={<IconSearch />} />

        {emptyResults.length > 0 &&
            <Alert status="warning">
                <AlertIcon />
                No results were found for: {[...new Set([...emptyResults])].join(', ')}
            </Alert>
        }

        {entities.includes('commands') &&
            <CommandsSearchResults keywords={keywords} emptyResultsSetter={setEmptyResults} />}
        {entities.includes('tasks') &&
            <TasksSearchResults keywords={keywords} emptyResultsSetter={setEmptyResults} />}
        {entities.includes('vulnerabilities') &&
            <VulnerabilitiesSearchResults keywords={keywords} emptyResultsSetter={setEmptyResults} />}
        {entities.includes('vulnerability_templates') &&
            <VulnerabilityTemplatesSearchResults keywords={keywords} emptyResultsSetter={setEmptyResults} />}
        {entities.includes('projects') &&
            <ProjectsSearchResults keywords={keywords} emptyResultsSetter={setEmptyResults} />}
        {entities.includes('project_templates') &&
            <ProjectTemplatesSearchResults keywords={keywords} emptyResultsSetter={setEmptyResults} />}
    </>
})
Example #7
Source File: index.jsx    From UpStats with MIT License 4 votes vote down vote up
export default function Dashboard() {
  const api = create({
    baseURL: "/api",
  });

  const toast = useToast();

  const router = useRouter();
  const [systems, setSystems] = useState([]);
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [mailing, setMailing] = useState(false);

  const [currentEditSystem, setCurrentEditSystem] = useState({
    name: "",
    url: "",
  });
  const [subsCount, setSubsCount] = useState(0);
  const loadSystems = async () => {
    try {
      const { data } = await http.get("/systems");
      setSystems(data);
    } catch (e) {
      toast({
        title: "Error",
        description: "Error Loading Systems",
        status: "error",
        duration: 9000,
        isClosable: true,
      });
    }
  };
  const loadConfig = async () => {
    try {
      const { data } = await http.get("/config");
      setMailing(data.mailing);
    } catch (e) {
      console.log("Error Loading Config");
    }
  };
  const loadCount = async () => {
    try {
      const { data } = await http.get("/subs");

      setSubsCount(data.length);
    } catch (e) {
      toast({
        title: "Error",
        description: "Error Loading Subs Count",
        status: "error",
        duration: 9000,
        isClosable: true,
      });
    }
  };
  useEffect(() => {
    const token = window.localStorage.getItem("token");
    http.setJwt(token);

    if (!token) {
      setIsLoggedIn(false);
      toast({
        title: "Error",
        description: "Redirecting to Login Page",
        status: "warning",
        duration: 9000,
        isClosable: true,
      });
      router.push("/login");
    } else setIsLoggedIn(true);
  }, []);
  useEffect(() => {
    loadSystems();
  }, []);

  useEffect(() => {
    loadCount();
  }, []);
  useEffect(() => {
    loadConfig();
  }, []);
  const handleDelete = async (system) => {
    const originalSystems = systems;
    const newSystems = originalSystems.filter((s) => s._id !== system._id);

    setSystems(newSystems);
    try {
      await http.delete(`/systems/${system._id}`);
    } catch (ex) {
      if (ex.response && ex.response.status === 404)
        toast({
          title: "Error",
          description: "System May be Already Deleted",
          status: "error",
          duration: 9000,
          isClosable: true,
        });
      setSystems(originalSystems);
    }
  };
  const handleAdd = async (system) => {
    try {
      const { data } = await api.post("/systems", system, {
        headers: localStorage.getItem("token"),
      });
      setSystems([...systems, data]);
    } catch (ex) {
      toast({
        title: "Error",
        description: "Submit Unsuccessful",
        status: "error",
        duration: 9000,
        isClosable: true,
      });
    }
  };

  const formik = useFormik({
    initialValues: {
      name: "",
      url: "",
      type: "web",
    },
    validationSchema: Yup.object({
      name: Yup.string()
        .max(15, "Must be 15 characters or less")
        .required("Required"),
      url: Yup.string().required("Required"),
      type: Yup.string(),
    }),
    onSubmit: (values) => {
      handleAdd(values);
      //alert(JSON.stringify(values, null, 2));
    },
  });
  const handleEdit = async () => {
    const originalSystems = systems;
    let newSystems = [...systems];
    const idx = newSystems.findIndex(
      (sys) => sys._id === currentEditSystem._id
    );
    newSystems[idx] = { ...currentEditSystem };

    setSystems(newSystems);
    try {
      await http.put(`/systems/${currentEditSystem._id}`, {
        name: currentEditSystem.name,
        url: currentEditSystem.url,
        type: currentEditSystem.type,
      });
      setCurrentEditSystem({ name: "", url: "" });
    } catch (ex) {
      toast({
        title: "Error",
        description: "Error Updating The System",
        status: "error",
        duration: 9000,
        isClosable: true,
      });
      setSystems(originalSystems);
      setCurrentEditSystem({ name: "", url: "" });
    }
  };
  const handleChangeConfig = async () => {
    try {
      await http.put(`/config`, {
        mailing: mailing,
      });
    } catch (ex) {
      toast({
        title: "Error",
        description: "Error Updating The Config",
        status: "error",
        duration: 9000,
        isClosable: true,
      });
    }
  };
  return (
    <FormikProvider value={formik}>
      <>
        <Layout>
          {isLoggedIn ? (
            <>
              <div className=" mt-12  mx-auto">
                <div>
                  <div className="m-auto p-4 md:w-1/4 sm:w-1/2 w-full">
                    <div className="p-12 py-6 rounded-lg">
                      <svg
                        fill="none"
                        stroke="currentColor"
                        strokeLinecap="round"
                        strokeLinejoin="round"
                        strokeWidth={2}
                        className="w-12 h-12 inline-block users-status"
                        viewBox="0 0 24 24"
                      >
                        <path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2" />
                        <circle cx={9} cy={7} r={4} />
                        <path d="M23 21v-2a4 4 0 00-3-3.87m-4-12a4 4 0 010 7.75" />
                      </svg>
                      <h2 className="title-font font-medium text-3xl">
                        {subsCount}
                      </h2>
                      <p className="leading-relaxed ">Users Subscribed</p>
                    </div>
                  </div>
                </div>
              </div>
              {/* CRUD Status List */}
              <div className="w-full max-w-sm overflow-hidden rounded-lg items-center mx-auto">
                <h3 className="text-2xl font-black text-black">
                  Add New System
                </h3>
                <form onSubmit={formik.handleSubmit} className="p-3">
                  <Stack>
                    <Text>System Title</Text>
                    <Input
                      id="name"
                      name="name"
                      type="text"
                      onChange={formik.handleChange}
                      onBlur={formik.handleBlur}
                      value={formik.values.name}
                      placeholder="Enter here"
                      isInvalid={
                        formik.touched.name && formik.errors.name ? true : false
                      }
                    />
                    {formik.touched.name && formik.errors.name ? (
                      <Alert status="error">
                        <AlertIcon />
                        {formik.errors.name}
                      </Alert>
                    ) : null}
                  </Stack>
                  <Stack mt={2}>
                    <Text>System URL</Text>
                    <Input
                      placeholder="Enter here"
                      id="url"
                      name="url"
                      type="text"
                      onChange={formik.handleChange}
                      onBlur={formik.handleBlur}
                      value={formik.values.url}
                      isInvalid={
                        formik.touched.url && formik.errors.url ? true : false
                      }
                    />
                    {formik.touched.url && formik.errors.url ? (
                      <Alert status="error">
                        <AlertIcon />
                        {formik.errors.url}
                      </Alert>
                    ) : null}
                  </Stack>
                  {/* Select System Type */}
                  <RadioGroup>
                    <Stack mt={5}>
                      <Field as={Radio} type="radio" name="type" value="web">
                        Web
                      </Field>
                      <Field
                        as={Radio}
                        type="radio"
                        name="type"
                        value="telegram"
                      >
                        Telegram Bot
                      </Field>
                    </Stack>
                  </RadioGroup>
                  {/* Add */}
                  <div className="mt-4">
                    <button
                      style={{ backgroundColor: "#3747D4" }}
                      className="px-4 py-2 text-white rounded hover:bg-black focus:outline-none"
                      type="submit"
                    >
                      Add
                    </button>
                  </div>
                </form>
                {/* Status Page List */}
                {/* Show Sites here */}
                {systems.map((system) => (
                  <div key={system._id} className="status-items-manage">
                    <div className="items">
                      <span className="site-title">{system?.name}</span>
                      <div className="i">
                        <EditIcon
                          mr="2"
                          onClick={() => {
                            setCurrentEditSystem(system);
                          }}
                        />
                        <DeleteIcon
                          color="red"
                          m="2"
                          onClick={() => {
                            handleDelete(system);
                          }}
                        />
                      </div>
                    </div>
                  </div>
                ))}
                {/* End */}
                {currentEditSystem.name ? (
                  <div className="mt-4">
                    <Stack>
                      <h3 className="text-2xl font-black text-black">
                        Edit System
                      </h3>
                      <Stack>
                        <Text>System Title</Text>
                        <Input
                          id="name"
                          name="name"
                          type="text"
                          value={currentEditSystem.name}
                          onChange={(e) => {
                            setCurrentEditSystem({
                              ...currentEditSystem,
                              name: e.target.value,
                            });
                          }}
                          placeholder="Enter here"
                        />
                      </Stack>
                      <Stack mt={2}>
                        <Text>System URL</Text>
                        <Input
                          placeholder="Enter here"
                          id="url"
                          name="url"
                          type="text"
                          value={currentEditSystem.url}
                          onChange={(e) =>
                            setCurrentEditSystem({
                              ...currentEditSystem,
                              url: e.target.value,
                            })
                          }
                        />
                      </Stack>
                    </Stack>
                    {/* Add */}
                    <div className="mt-4">
                      <button
                        onClick={handleEdit}
                        style={{ backgroundColor: "#3747D4" }}
                        className="px-4 py-2 text-white rounded hover:bg-black focus:outline-none"
                      >
                        Done
                      </button>
                    </div>
                  </div>
                ) : (
                  ""
                )}
                <Stack mt={12}>
                  <h3 className="text-xl font-black text-bold">Configs</h3>
                  <p className="text-md font-black text-bold">Mailing</p>
                  <Switch
                    size="lg"
                    isChecked={mailing}
                    onChange={(e) => setMailing(e.target.checked)}
                  />
                  <div className="mt-4">
                    <button
                      onClick={handleChangeConfig}
                      style={{ backgroundColor: "#3747D4" }}
                      className="px-4 py-2 text-white rounded hover:bg-black focus:outline-none"
                    >
                      Done
                    </button>
                  </div>
                </Stack>
              </div>
            </>
          ) : (
            ""
          )}

          {/* Total No. of Users Subscribed */}
        </Layout>
      </>
    </FormikProvider>
  );
}
Example #8
Source File: manage-admin.jsx    From UpStats with MIT License 4 votes vote down vote up
Test = (props) => {
  const api = create({
    baseURL: `/api`,
  });
  const toast = useToast();

  const router = useRouter();
  const [users, setUsers] = useState([]);
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  const [currentEditUser, setCurrentEditUser] = useState({
    email: "",
  });
  const loadUsers = async () => {
    try {
      const { data } = await http.get("/users");
      setUsers(data);
    } catch (e) {
      toast({
        title: "Error",
        description: "Error Loading Users",
        status: "error",
        duration: 9000,
        isClosable: true,
      });
    }
  };
  useEffect(() => {
    const token = window.localStorage.getItem("token");
    http.setJwt(token);

    if (!token) {
      setIsLoggedIn(false);
      toast({
        title: "Error",
        description: "Redirecting to Login Page",
        status: "warning",
        duration: 9000,
        isClosable: true,
      });
      router.push("/login");
    } else setIsLoggedIn(true);
  }, []);

  useEffect(() => {
    loadUsers();
  }, []);

  const handleDelete = async (user) => {
    const originalUsers = users;
    const newUsers = originalUsers.filter((s) => s._id !== user._id);

    setUsers(newUsers);
    try {
      await http.delete(`/users/${user._id}`);
    } catch (ex) {
      if (ex.response && ex.response.status === 404)
        toast({
          title: "Error",
          description: "User May be Already Deleted",
          status: "error",
          duration: 9000,
          isClosable: true,
        });
      setUsers(originalUsers);
    }
  };
  const handleAdd = async (user) => {
    try {
      const { data } = await api.post("/users", user, {
        headers: localStorage.getItem("token"),
      });
      setUsers([...users, data]);
    } catch (ex) {
      toast({
        title: "Error",
        description: "Submit Unsuccessful",
        status: "error",
        duration: 9000,
        isClosable: true,
      });
    }
  };

  const handleEdit = async () => {
    const originalUsers = users;
    let newUsers = [...users];
    const idx = newUsers.findIndex((sys) => sys._id === currentEditUser._id);
    newUsers[idx] = { ...currentEditUser };

    setUsers(newUsers);
    try {
      await http.put(`/users/${currentEditUser._id}`, {
        email: currentEditUser.email,
      });
      setCurrentEditUser({ email: "" });
    } catch (ex) {
      toast({
        title: "Error",
        description: "Error Updating The User",
        status: "error",
        duration: 9000,
        isClosable: true,
      });
      setUsers(originalUsers);
      setCurrentEditUser({ email: "" });
    }
  };
  const formik = useFormik({
    initialValues: {
      email: "",
    },
    validationSchema: Yup.object({
      email: Yup.string().label("Email").email().required("Required"),
    }),
    onSubmit: (values) => {
      handleAdd(values);
    },
  });
  return (
    <FormikProvider value={formik}>
      <Layout>
        <>
          {isLoggedIn ? (
            <>
              {/* CRUD Status List */}
              <div className="w-full max-w-sm overflow-hidden rounded-lg items-center mx-auto">
                <h3 className="text-2xl font-black text-black">New Admin</h3>
                <form onSubmit={formik.handleSubmit} className="p-3">
                  <Stack>
                    <Text>Email</Text>
                    <Input
                      id="email"
                      name="email"
                      type="text"
                      onChange={formik.handleChange}
                      onBlur={formik.handleBlur}
                      value={formik.values.email}
                      placeholder="Enter here"
                      isInvalid={
                        formik.touched.email && formik.errors.email
                          ? true
                          : false
                      }
                    />
                    {formik.touched.email && formik.errors.email ? (
                      <Alert status="error">
                        <AlertIcon />
                        {formik.errors.email}
                      </Alert>
                    ) : null}
                  </Stack>
                  {/* Add */}
                  <div className="mt-4">
                    <button
                      style={{ backgroundColor: "#3747D4" }}
                      className="px-4 py-2 text-white rounded hover:bg-black focus:outline-none"
                      type="submit"
                    >
                      Add
                    </button>
                  </div>
                </form>
                {users.map((user) => (
                  <div key={user._id} className="status-items-manage">
                    <div className="items">
                      <span className="site-title">{user.name}</span>
                      <div className="i">
                        <EditIcon
                          mr="2"
                          onClick={() => {
                            setCurrentEditUser(user);
                          }}
                        />
                        <DeleteIcon
                          color="red"
                          m="2"
                          onClick={() => {
                            handleDelete(user);
                          }}
                        />
                      </div>
                    </div>
                  </div>
                ))}
                {/* End */}
                {currentEditUser.email ? (
                  <div className="mt-4">
                    <Stack>
                      <h3 className="text-2xl font-black text-black">
                        Edit User
                      </h3>
                      <Stack mt={2}>
                        <Text>Email</Text>
                        <Input
                          placeholder="Enter here"
                          id="email"
                          name="email"
                          type="text"
                          value={currentEditUser.email}
                          onChange={(e) =>
                            setCurrentEditUser({
                              ...currentEditUser,
                              email: e.target.value,
                            })
                          }
                        />
                      </Stack>
                    </Stack>
                    {/* Add */}
                    <div className="mt-4">
                      <button
                        onClick={handleEdit}
                        style={{ backgroundColor: "#3747D4" }}
                        className="px-4 py-2 text-white rounded hover:bg-black focus:outline-none"
                      >
                        Done
                      </button>
                    </div>
                  </div>
                ) : (
                  ""
                )}
              </div>
            </>
          ) : (
            ""
          )}
        </>
      </Layout>
    </FormikProvider>
  );
}
Example #9
Source File: login.jsx    From UpStats with MIT License 4 votes vote down vote up
Login = (props) => {
  const toast = useToast();
  const login = async (email, password) => {
    try {
      const { data: jwt } = await http.post("/auth", { email, password });
      window.localStorage.setItem(tokenKey, jwt);
      window.location = "/admin";
      toast({
        title: "Success",
        description: "Redirecting...",
        status: "success",
        duration: 9000,
        isClosable: true,
      });
    } catch (ex) {
      toast({
        title: "Error",
        description: "Cannot Login to Account",
        status: "error",
        duration: 9000,
        isClosable: true,
      });
    }
  };
  useEffect(() => {
    const token = window.localStorage.getItem("token");

    if (token) {
      window.location = "/admin";
    }
  }, []);

  const formik = useFormik({
    initialValues: {
      email: "",
      password: "",
    },
    validationSchema: Yup.object({
      email: Yup.string().email().label("Email").required(),
      password: Yup.string().label("Password").required(),
    }),
    onSubmit: (values) => {
      login(values.email, values.password);

      //alert(JSON.stringify(values, null, 2));
    },
  });
  return (
    <div className="w-full max-w-sm mx-auto overflow-hidden rounded-lg">
      <div className="px-6 py-4">
        <h2 className="mt-1 text-3xl font-medium text-center">Welcome Back</h2>
        <p className="mt-1 text-center">Login to continue</p>
        <form onSubmit={formik.handleSubmit}>
          <Stack>
            <Text>Email</Text>
            <Input
              id="email"
              name="email"
              type="text"
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              value={formik.values.email}
              placeholder="Enter here"
              isInvalid={
                formik.touched.email && formik.errors.email ? true : false
              }
            />
            {formik.touched.email && formik.errors.email ? (
              <Alert status="error">
                <AlertIcon />
                {formik.errors.email}
              </Alert>
            ) : null}
          </Stack>
          <Stack>
            <Text>Password</Text>
            <Input
              id="password"
              name="password"
              type="password"
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              value={formik.values.password}
              placeholder="Enter here"
              isInvalid={
                formik.touched.password && formik.errors.password ? true : false
              }
            />
            {formik.touched.password && formik.errors.password ? (
              <Alert status="error">
                <AlertIcon />
                {formik.errors.password}
              </Alert>
            ) : null}
          </Stack>
          {/* Login */}
          <div className="flex items-center  mt-4">
            <button
              style={{ backgroundColor: "#3747D4" }}
              className="px-4 py-2 text-white rounded hover:bg-black focus:outline-none"
              type="submit"
            >
              Login
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}
Example #10
Source File: register.jsx    From UpStats with MIT License 4 votes vote down vote up
Register = (props) => {
  const toast = useToast();
  const register = async (creds) => {
    try {
      const { data: jwt } = await http.post("/users/create", { ...creds });
      window.localStorage.setItem(tokenKey, jwt);
      window.location = "/admin";
      toast({
        title: "Success",
        description: "Redirecting...",
        status: "success",
        duration: 9000,
        isClosable: true,
      });
    } catch (ex) {
      toast({
        title: "Error",
        description: "Cannot Login to Account",
        status: "error",
        duration: 9000,
        isClosable: true,
      });
    }
  };
  useEffect(() => {
    const token = window.localStorage.getItem("token");

    if (token) {
      window.location = "/admin";
    }
  }, []);

  const formik = useFormik({
    initialValues: {
      name: "",
      email: "",
      password: "",
    },
    validationSchema: Yup.object({
      name: Yup.string().label("Name").required(),
      email: Yup.string().email().label("Email").required(),
      password: Yup.string().label("Password").required(),
    }),
    onSubmit: (values) => {
      register(values);

      //alert(JSON.stringify(values, null, 2));
    },
  });
  return (
    <div className="w-full max-w-sm mx-auto overflow-hidden rounded-lg">
      <div className="px-6 py-4">
        <h2 className="mt-1 text-3xl font-medium text-center">Welcome Back</h2>
        <p className="mt-1 text-center">Login to continue</p>
        <form onSubmit={formik.handleSubmit}>
          <Stack>
            <Text>Name</Text>
            <Input
              id="name"
              name="name"
              type="text"
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              value={formik.values.name}
              placeholder="Enter here"
              isInvalid={
                formik.touched.name && formik.errors.name ? true : false
              }
            />
            {formik.touched.name && formik.errors.name ? (
              <Alert status="error">
                <AlertIcon />
                {formik.errors.name}
              </Alert>
            ) : null}
          </Stack>
          <Stack>
            <Text>Email</Text>
            <Input
              id="email"
              name="email"
              type="text"
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              value={formik.values.email}
              placeholder="Enter here"
              isInvalid={
                formik.touched.email && formik.errors.email ? true : false
              }
            />
            {formik.touched.email && formik.errors.email ? (
              <Alert status="error">
                <AlertIcon />
                {formik.errors.email}
              </Alert>
            ) : null}
          </Stack>
          <Stack>
            <Text>Password</Text>
            <Input
              id="password"
              name="password"
              type="password"
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              value={formik.values.password}
              placeholder="Enter here"
              isInvalid={
                formik.touched.password && formik.errors.password ? true : false
              }
            />
            {formik.touched.password && formik.errors.password ? (
              <Alert status="error">
                <AlertIcon />
                {formik.errors.password}
              </Alert>
            ) : null}
          </Stack>
          {/* Register */}
          <div className="flex items-center  mt-4">
            <button
              style={{ backgroundColor: "#3747D4" }}
              className="px-4 py-2 text-white rounded hover:bg-black focus:outline-none"
              type="submit"
            >
              Register
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}
Example #11
Source File: list.js    From idena-web with MIT License 4 votes vote down vote up
export default function FlipListPage() {
  const {t} = useTranslation()

  const toast = useToast()

  const epochState = useEpoch()
  const {privateKey} = useAuthState()

  const {
    isOpen: isOpenDeleteForm,
    onOpen: openDeleteForm,
    onClose: onCloseDeleteForm,
  } = useDisclosure()

  const [
    {
      flips: knownFlips,
      requiredFlips: requiredFlipsNumber,
      availableFlips: availableFlipsNumber,
      state: status,
    },
  ] = useIdentity()

  const [selectedFlip, setSelectedFlip] = React.useState()

  const canSubmitFlips = [
    IdentityStatus.Verified,
    IdentityStatus.Human,
    IdentityStatus.Newbie,
  ].includes(status)

  const [current, send] = useMachine(flipsMachine, {
    context: {
      knownFlips: knownFlips || [],
      filter: loadPersistentState('flipFilter') || FlipFilterType.Active,
    },
    actions: {
      onError: (_, {error}) =>
        toast({
          title: error,
          status: 'error',
          duration: 5000,
          isClosable: true,
          // eslint-disable-next-line react/display-name
          render: () => (
            <Box fontSize="md">
              <Notification title={error} type={NotificationType.Error} />
            </Box>
          ),
        }),
    },
    logger: msg => console.log(redact(msg)),
  })

  useEffect(() => {
    if (epochState && privateKey && status) {
      send('INITIALIZE', {epoch: epochState.epoch, privateKey, canSubmitFlips})
    }
  }, [canSubmitFlips, epochState, privateKey, send, status])

  const {flips, missingFlips, filter} = current.context

  const filterFlips = () => {
    switch (filter) {
      case FlipFilterType.Active:
        return flips.filter(({type}) =>
          [
            FlipType.Publishing,
            FlipType.Published,
            FlipType.Deleting,
            FlipType.Invalid,
          ].includes(type)
        )
      case FlipType.Draft:
        return flips.filter(({type}) => type === FlipType.Draft)
      case FlipType.Archived:
        return flips.filter(({type}) =>
          [FlipType.Archived, FlipType.Deleted].includes(type)
        )
      default:
        return []
    }
  }

  const madeFlipsNumber = (knownFlips || []).length

  const remainingRequiredFlips = requiredFlipsNumber - madeFlipsNumber
  const remainingOptionalFlips =
    availableFlipsNumber - Math.max(requiredFlipsNumber, madeFlipsNumber)

  const [currentOnboarding, {dismissCurrentTask}] = useOnboarding()

  const eitherOnboardingState = (...states) =>
    eitherState(currentOnboarding, ...states)

  return (
    <Layout>
      <Page pt={[4, 6]}>
        <MobileApiStatus display={['initial', 'none']} left={4} />
        <PageTitleNew>{t('My Flips')}</PageTitleNew>
        <Flex justify="space-between" align="center" alignSelf="stretch" mb={8}>
          <Stack spacing={2} isInline>
            <Button
              variant="tab"
              onClick={() => send('FILTER', {filter: FlipFilterType.Active})}
              isActive={filter === FlipFilterType.Active}
            >
              {t('Active')}
            </Button>
            <Button
              variant="tab"
              onClick={() => send('FILTER', {filter: FlipFilterType.Draft})}
              isActive={filter === FlipFilterType.Draft}
            >
              {t('Drafts')}
            </Button>
            <Button
              variant="tab"
              onClick={() => send('FILTER', {filter: FlipFilterType.Archived})}
              isActive={filter === FlipFilterType.Archived}
            >
              {t('Archived')}
            </Button>
          </Stack>
          <Box alignSelf="end">
            <OnboardingPopover
              isOpen={eitherOnboardingState(
                onboardingShowingStep(OnboardingStep.CreateFlips)
              )}
            >
              <PopoverTrigger>
                <Box onClick={dismissCurrentTask}>
                  <IconLink
                    icon={<PlusSolidIcon boxSize={5} mt={1} />}
                    href="/flips/new"
                    bg="white"
                    position={
                      eitherOnboardingState(
                        onboardingShowingStep(OnboardingStep.CreateFlips)
                      )
                        ? 'relative'
                        : 'initial'
                    }
                    zIndex={2}
                  >
                    {t('New flip')}
                  </IconLink>
                </Box>
              </PopoverTrigger>
              <OnboardingPopoverContent
                title={t('Create required flips')}
                onDismiss={dismissCurrentTask}
              >
                <Stack>
                  <Text>
                    {t(`You need to create at least 3 flips per epoch to participate
                    in the next validation ceremony. Follow step-by-step
                    instructions.`)}
                  </Text>
                  <OnboardingPopoverContentIconRow
                    icon={<RewardIcon boxSize={5} />}
                  >
                    {t(
                      `You'll get rewarded for every successfully qualified flip.`
                    )}
                  </OnboardingPopoverContentIconRow>
                  <OnboardingPopoverContentIconRow
                    icon={<PenaltyIcon boxSize={5} />}
                  >
                    {t(`Read carefully "What is a bad flip" rules to avoid
                      penalty.`)}
                  </OnboardingPopoverContentIconRow>
                </Stack>
              </OnboardingPopoverContent>
            </OnboardingPopover>
          </Box>
        </Flex>
        {current.matches('ready.dirty.active') &&
          canSubmitFlips &&
          (remainingRequiredFlips > 0 || remainingOptionalFlips > 0) && (
            <Box alignSelf="stretch" mb={8}>
              <Alert
                status="success"
                bg="green.010"
                borderWidth="1px"
                borderColor="green.050"
                fontWeight={500}
                rounded="md"
                px={3}
                py={2}
              >
                <AlertIcon name="info" color="green.500" size={5} mr={3} />
                {remainingRequiredFlips > 0
                  ? t(
                      `Please submit {{remainingRequiredFlips}} required flips.`,
                      {remainingRequiredFlips}
                    )
                  : null}{' '}
                {remainingOptionalFlips > 0
                  ? t(
                      `You can also submit {{remainingOptionalFlips}} optional flips if you want.`,
                      {
                        remainingOptionalFlips,
                      }
                    )
                  : null}
              </Alert>
            </Box>
          )}

        {status && !canSubmitFlips && (
          <Box alignSelf="stretch" mb={8}>
            <Alert
              status="error"
              bg="red.010"
              borderWidth="1px"
              borderColor="red.050"
              fontWeight={500}
              rounded="md"
              px={3}
              py={2}
            >
              <AlertIcon
                name="info"
                color="red.500"
                size={5}
                mr={3}
              ></AlertIcon>
              {t('You can not submit flips. Please get validated first. ')}
            </Alert>
          </Box>
        )}

        {current.matches('ready.pristine') && (
          <Flex
            flex={1}
            alignItems="center"
            justifyContent="center"
            alignSelf="stretch"
          >
            <Image src="/static/flips-cant-icn.svg" />
          </Flex>
        )}

        {current.matches('ready.dirty') && (
          <FlipCardList>
            {filterFlips().map(flip => (
              <FlipCard
                key={flip.id}
                flipService={flip.ref}
                onDelete={() => {
                  if (
                    flip.type === FlipType.Published &&
                    (knownFlips || []).includes(flip.hash)
                  ) {
                    setSelectedFlip(flip)
                    openDeleteForm()
                  } else flip.ref.send('ARCHIVE')
                }}
              />
            ))}
            {current.matches('ready.dirty.active') && (
              <>
                {missingFlips.map(({keywords}, idx) => (
                  <Box key={idx}>
                    <EmptyFlipBox>
                      <Image src="/static/flips-cant-icn.svg" />
                    </EmptyFlipBox>
                    <Box mt={4}>
                      <FlipCardTitle>
                        {keywords
                          ? formatKeywords(keywords.words)
                          : t('Missing keywords')}
                      </FlipCardTitle>
                      <FlipCardSubtitle>
                        {t('Missing on client')}
                      </FlipCardSubtitle>
                    </Box>
                  </Box>
                ))}
                {Array.from({length: remainingRequiredFlips}, (flip, idx) => (
                  <RequiredFlipPlaceholder
                    key={idx}
                    title={`Flip #${madeFlipsNumber + idx + 1}`}
                    {...flip}
                  />
                ))}
                {Array.from({length: remainingOptionalFlips}, (flip, idx) => (
                  <OptionalFlipPlaceholder
                    key={idx}
                    title={`Flip #${availableFlipsNumber -
                      (remainingOptionalFlips - idx - 1)}`}
                    {...flip}
                    isDisabled={remainingRequiredFlips > 0}
                  />
                ))}
              </>
            )}
          </FlipCardList>
        )}

        <DeleteFlipDrawer
          hash={selectedFlip?.hash}
          cover={selectedFlip?.images[selectedFlip.originalOrder[0]]}
          isOpen={isOpenDeleteForm}
          onClose={onCloseDeleteForm}
          onDelete={() => {
            selectedFlip.ref.send('DELETE')
            onCloseDeleteForm()
          }}
        />
      </Page>
    </Layout>
  )
}
Example #12
Source File: node.js    From idena-web with MIT License 4 votes vote down vote up
function Settings() {
  const {t} = useTranslation()
  const {addNotification} = useNotificationDispatch()
  const settingsState = useSettingsState()
  const {saveConnection} = useSettingsDispatch()

  const size = useBreakpointValue(['lg', 'md'])
  const flexDirection = useBreakpointValue(['column', 'row'])
  const flexJustify = useBreakpointValue(['flex-start', 'space-between'])

  const [state, setState] = useState({
    url: settingsState.url || '',
    apiKey: settingsState.apiKey || '',
  })

  const [nodeProvider, setNodeProvider] = useState('')

  useEffect(() => {
    setState({url: settingsState.url, apiKey: settingsState.apiKey})
  }, [settingsState])

  const notify = () =>
    addNotification({
      title: 'Settings updated',
      body: `Connected to url ${state.url}`,
    })

  useEffect(() => {
    async function check() {
      try {
        const result = await checkKey(settingsState.apiKey)
        const provider = await getProvider(result.provider)
        setNodeProvider(provider.data.ownerName)
      } catch (e) {}
    }

    if (settingsState.apiKeyState === ApiKeyStates.OFFLINE) check()
  }, [settingsState.apiKeyState, settingsState.url, settingsState.apiKey])

  return (
    <SettingsLayout title={t('Node')}>
      <Stack
        spacing={5}
        mt={[3, 8]}
        width={['100%', '480px']}
        position="relative"
      >
        {settingsState.apiKeyState === ApiKeyStates.RESTRICTED && (
          <Alert
            status="error"
            bg="red.010"
            borderWidth="1px"
            borderColor="red.050"
            fontWeight={500}
            rounded="md"
            px={3}
            py={2}
          >
            <AlertIcon name="info" color="red.500" size={5} mr={3}></AlertIcon>
            {t(
              'The shared node access is restricted. You cannot use the node for the upcoming validation ceremony.'
            )}
          </Alert>
        )}
        {settingsState.apiKeyState === ApiKeyStates.OFFLINE &&
          !!settingsState.url &&
          !!settingsState.apiKey && (
            <Alert
              status="error"
              bg="red.010"
              borderWidth="1px"
              borderColor="red.050"
              fontWeight={500}
              rounded="md"
              px={3}
              py={2}
            >
              <AlertIcon name="info" color="red.500" size={5} mr={3} />
              <Text>
                {nodeProvider
                  ? t(
                      'This node is unavailable. Please contact the node owner:',
                      {
                        nsSeparator: 'null',
                      }
                    )
                  : t('Node is unavailable.')}{' '}
                {nodeProvider && (
                  <Link
                    color="#578fff"
                    href={`https://t.me/${nodeProvider}`}
                    target="_blank"
                    ml={1}
                  >
                    {nodeProvider}
                  </Link>
                )}
              </Text>
            </Alert>
          )}
        <Flex display={['none', 'flex']} justify="space-between">
          <Heading as="h1" fontSize="lg" fontWeight={500} textAlign="start">
            {t('Node settings')}
          </Heading>
          <Box mt="3px">
            <Link
              color="#578fff"
              fontSize="13px"
              fontWeight="500"
              height="17px"
              href="/node/rent"
            >
              {t('Rent a new node')}
              <ChevronDownIcon boxSize={4} transform="rotate(-90deg)" />
            </Link>
          </Box>
        </Flex>
        <FormControl
          as={Flex}
          direction={flexDirection}
          justify={flexJustify}
          mt={[0, 5]}
        >
          <Flex justify="space-between">
            <FormLabel
              fontSize={['base', 'md']}
              color={['brandGray.500', 'muted']}
              fontWeight={[500, 400]}
              mb={[2, 0]}
              lineHeight={[6, 8]}
            >
              {t('Shared node URL')}
            </FormLabel>
            <Box display={['block', 'none']}>
              <Link
                fontSize="16px"
                fontWeight="500"
                color="#578fff"
                href="/node/rent"
              >
                {t('Rent a new node')}
              </Link>
            </Box>
          </Flex>
          <Input
            id="url"
            w={['100%', '360px']}
            size={size}
            value={state.url}
            onChange={e => setState({...state, url: e.target.value})}
          />
        </FormControl>
        <FormControl as={Flex} direction={flexDirection} justify={flexJustify}>
          <FormLabel
            fontSize={['base', 'md']}
            color={['brandGray.500', 'muted']}
            fontWeight={[500, 400]}
            mb={[2, 0]}
            lineHeight={[6, 8]}
          >
            {t('Node API key')}
          </FormLabel>
          <PasswordInput
            id="key"
            w={['100%', '360px']}
            size={size}
            value={state.apiKey}
            onChange={e => setState({...state, apiKey: e.target.value})}
          />
        </FormControl>

        {settingsState.apiKeyState === ApiKeyStates.ONLINE && (
          <Alert
            status="warning"
            bg="warning.020"
            borderWidth="1px"
            borderColor="warning.100"
            fontWeight={500}
            rounded="md"
            px={3}
            py={2}
          >
            <AlertIcon size={5} mr={3} colo="warning.500"></AlertIcon>
            {t(
              'Please do not use the API key on multiple devices at the same time as this will cause the validation failure.'
            )}
          </Alert>
        )}

        <Flex justify="space-between">
          <PrimaryButton
            size={size}
            w={['100%', 'auto']}
            onClick={() => {
              saveConnection(state.url, state.apiKey, true)
              notify()
            }}
            ml="auto"
          >
            {t('Save')}
          </PrimaryButton>
        </Flex>
      </Stack>
    </SettingsLayout>
  )
}
Example #13
Source File: containers.js    From idena-web with MIT License 4 votes vote down vote up
export function DnaSendFailedDialog({
  error,
  url,
  onRetrySucceeded,
  onRetryFailed,
  onOpenFailUrl,
  ...props
}) {
  const {t} = useTranslation()
  return (
    <Dialog closeOnOverlayClick={false} closeOnEsc={false} {...props}>
      <DialogBody>
        <Stack spacing={5}>
          <Alert
            status="error"
            bg="red.010"
            borderRadius="lg"
            flexDirection="column"
            justifyContent="center"
            textAlign="center"
            minH={132}
          >
            <Stack align="center" spacing={1}>
              <AlertIcon name="delete" size={10} mr={0} />
              <Stack spacing={1}>
                <AlertTitle fontSize="lg" fontWeight={500}>
                  {t('Something went wrong')}
                </AlertTitle>
                <Text color="muted" wordBreak="break-all">
                  {error}
                </Text>
              </Stack>
            </Stack>
          </Alert>
        </Stack>
      </DialogBody>
      <DialogFooter>
        <SecondaryButton
          onClick={() => {
            const requestedUrl = new URL(url)
            handleCallbackUrl(url, 'json', {
              // eslint-disable-next-line no-shadow
              onJson: ({success, error, url}) => {
                if (success) {
                  onRetrySucceeded({
                    hash: requestedUrl.searchParams.get('tx'),
                    url: url ?? requestedUrl.href,
                  })
                } else {
                  onRetryFailed({
                    error:
                      error ??
                      t('{{url}} responded with an unknown format', {
                        url: requestedUrl.href,
                      }),
                    url: url ?? requestedUrl,
                  })
                }
              },
            }).catch(error => {
              console.error(error)
              onRetryFailed({
                error: error?.message,
                url,
              })
            })
          }}
        >
          {t('Retry')}
        </SecondaryButton>
        <PrimaryButton
          onClick={() => {
            openExternalUrl(url)
            onOpenFailUrl()
          }}
        >
          {t('Open in browser')}
        </PrimaryButton>
      </DialogFooter>
    </Dialog>
  )
}
Example #14
Source File: components.js    From idena-web with MIT License 4 votes vote down vote up
export function DeactivateMiningDrawer({
  isLoading,
  delegatee,
  canUndelegate,
  onDeactivate,
  onClose,
  ...props
}) {
  const {t} = useTranslation()

  const sizeInput = useBreakpointValue(['lg', 'md'])
  const sizeButton = useBreakpointValue(['mdx', 'md'])
  const variantPrimary = useBreakpointValue(['primaryFlat', 'primary'])
  const variantSecondary = useBreakpointValue(['secondaryFlat', 'secondary'])

  const isDelegator = typeof delegatee === 'string'

  return (
    <AdDrawer isMining={isLoading} onClose={onClose} {...props}>
      <DrawerHeader>
        <Flex
          direction="column"
          textAlign={['center', 'start']}
          justify={['space-between', 'flex-start']}
        >
          <Flex
            order={[2, 1]}
            mt={[6, 0]}
            align="center"
            justify="center"
            bg="blue.012"
            h={12}
            w={12}
            rounded="xl"
          >
            <UserIcon boxSize={6} color="blue.500" />
          </Flex>
          <Heading
            order={[1, 2]}
            color="brandGray.500"
            fontSize={['base', 'lg']}
            fontWeight={[['bold', 500]]}
            lineHeight="base"
            mt={[0, 4]}
          >
            {isDelegator
              ? t('Deactivate delegation status')
              : t('Deactivate mining status')}
          </Heading>
        </Flex>
      </DrawerHeader>
      <DrawerBody>
        <Stack spacing={6} mt={[2, 30]}>
          <Text fontSize={['mdx', 'md']} mb={[0, 3]}>
            {isDelegator
              ? t(`Submit the form to deactivate your delegation status.`)
              : t(
                  `Submit the form to deactivate your mining status. You can activate it again afterwards.`
                )}
          </Text>
          {isDelegator && (
            <FormControl as={Stack} spacing={[0, 3]}>
              <FormLabel fontSize={['base', 'md']}>
                {t('Delegation address')}
              </FormLabel>
              <Input size={sizeInput} defaultValue={delegatee} isDisabled />
            </FormControl>
          )}
          {isDelegator && !canUndelegate && (
            <Alert
              status="error"
              rounded="md"
              bg="red.010"
              borderColor="red.050"
              borderWidth={1}
            >
              <AlertIcon name="info" alignSelf="flex-start" color="red.500" />
              <AlertDescription
                color="brandGray.500"
                fontSize="md"
                fontWeight={500}
              >
                {t('You can disable delegation at the next epoch only')}
              </AlertDescription>
            </Alert>
          )}
        </Stack>

        <PrimaryButton
          display={['flex', 'none']}
          mt={4}
          w="100%"
          fontSize="mobile"
          size="lg"
          isDisabled={isDelegator && !canUndelegate}
          isLoading={isLoading}
          onClick={onDeactivate}
          loadingText={t('Waiting...')}
        >
          {t('Submit')}
        </PrimaryButton>
      </DrawerBody>
      <DrawerFooter display={['none', 'flex']} mt={[6, 0]} px={0}>
        <Flex width="100%" justify={['space-evenly', 'flex-end']}>
          <Button
            variant={variantSecondary}
            order={[3, 1]}
            size={sizeButton}
            type="button"
            onClick={onClose}
          >
            {t('Cancel')}
          </Button>
          <Divider
            order="2"
            display={['block', 'none']}
            h={10}
            orientation="vertical"
            color="gray.100"
          />
          <Button
            variant={variantPrimary}
            order={[1, 3]}
            size={sizeButton}
            ml={[0, 2]}
            isDisabled={isDelegator && !canUndelegate}
            isLoading={isLoading}
            onClick={onDeactivate}
            loadingText={t('Waiting...')}
          >
            {t('Submit')}
          </Button>
        </Flex>
      </DrawerFooter>
    </AdDrawer>
  )
}
Example #15
Source File: components.js    From idena-web with MIT License 4 votes vote down vote up
export function ReviewValidationDialog({
  flips,
  reportedFlipsCount,
  availableReportsCount,
  isSubmitting,
  onSubmit,
  onMisingAnswers,
  onMisingReports,
  onCancel,
  isDesktop,
  ...props
}) {
  const {t} = useTranslation()

  const answeredFlipsCount = flips.filter(({option}) => option > 0).length

  const areFlipsUnanswered = answeredFlipsCount < flips.length
  const areReportsMissing = reportedFlipsCount < availableReportsCount

  const NoticeFooter = isDesktop ? DialogFooter : DrawerFooter
  const size = useBreakpointValue(['mdx', 'md'])
  const variantPrimary = useBreakpointValue(['primaryFlat', 'primary'])
  const variantSecondary = useBreakpointValue(['secondaryFlat', 'secondary'])

  const approvedCount = flips.filter(
    flip => flip.relevance === RelevanceType.Relevant
  ).length

  const abstainedCount = flips.filter(
    flip =>
      (flip.relevance ?? RelevanceType.Abstained) === RelevanceType.Abstained
  ).length

  return (
    <Dialog
      title={t('Submit the answers')}
      onClose={onCancel}
      isDesktop={isDesktop}
      isCloseable={false}
      {...props}
    >
      <ValidationDialogBody>
        <Stack spacing={6}>
          <Stack spacing={4}>
            <Stack spacing={2}>
              <ReviewValidationDialog.Stat
                label={t('Answered')}
                value={t('{{answeredFlips}} out of {{totalFlips}}', {
                  answeredFlips: answeredFlipsCount,
                  totalFlips: flips.length,
                })}
              />
              <ReviewValidationDialog.Stat
                label={t('Approved')}
                value={approvedCount}
              />
              <ReviewValidationDialog.Stat
                label={t('Reported')}
                value={reportedFlipsCount}
              />
              {availableReportsCount - reportedFlipsCount > 0 ? (
                <ReviewValidationDialog.Stat
                  label={t('Unused reports')}
                  value={availableReportsCount - reportedFlipsCount}
                />
              ) : (
                <ReviewValidationDialog.Stat
                  label={t('Abstained')}
                  value={abstainedCount}
                />
              )}
            </Stack>
            {(areFlipsUnanswered || areReportsMissing) && (
              <Stack>
                {areFlipsUnanswered && (
                  <Text color="muted">
                    <Trans i18nKey="reviewMissingFlips" t={t}>
                      You need to answer{' '}
                      <ReviewValidationDialog.LinkButton
                        onClick={onMisingAnswers}
                      >
                        all flips
                      </ReviewValidationDialog.LinkButton>{' '}
                      otherwise you may fail the validation.
                    </Trans>
                  </Text>
                )}
                {areReportsMissing && (
                  <Text color="muted">
                    <Trans i18nKey="reviewMissingReports" t={t}>
                      Use{' '}
                      <ReviewValidationDialog.LinkButton
                        variant="link"
                        onClick={onMisingReports}
                      >
                        all available reports
                      </ReviewValidationDialog.LinkButton>{' '}
                      to get maximum rewards.
                    </Trans>
                  </Text>
                )}
              </Stack>
            )}
          </Stack>
          {areReportsMissing && (
            <Alert
              status="error"
              bg="red.010"
              borderWidth="1px"
              borderColor="red.050"
              fontWeight={500}
              rounded="md"
              px={3}
              py={2}
            >
              <AlertIcon name="info" color="red.500" size={5} mr={3} />
              {t('You may lose rewards. Are you sure?')}
            </Alert>
          )}
        </Stack>
      </ValidationDialogBody>
      <NoticeFooter {...props}>
        <Button
          variant={variantSecondary}
          size={size}
          w={['100%', 'auto']}
          onClick={onCancel}
        >
          {t('Cancel')}
        </Button>
        <Divider
          display={['block', 'none']}
          h={10}
          orientation="vertical"
          color="gray.100"
        />
        <Button
          variant={variantPrimary}
          size={size}
          w={['100%', 'auto']}
          isLoading={isSubmitting}
          loadingText={t('Submitting answers...')}
          onClick={onSubmit}
        >
          {isDesktop ? t('Submit answers') : t('Submit')}
        </Button>
      </NoticeFooter>
    </Dialog>
  )
}
Example #16
Source File: Membership.js    From web-client with Apache License 2.0 4 votes vote down vote up
ProjectMembership = () => {
    const { projectId } = useParams();
    const [users] = useFetch(`/users`)
    const [members, updateMembers] = useFetch(`/projects/${projectId}/users`)
    const [savedProject] = useFetch(`/projects/${projectId}`);
    const [availableUsers, setAvailableUsers] = useState([]);

    const handleOnClick = ev => {
        ev.preventDefault();

        const userId = document.getElementById('userId').value;
        const userData = { userId: userId };
        secureApiFetch(`/projects/${projectId}/users`, {
            method: 'POST',
            body: JSON.stringify(userData)
        }).then(() => {
            updateMembers()
        })
    }

    const handleDelete = (member) => {
        secureApiFetch(`/projects/${projectId}/users/${member.membership_id}`, {
            method: 'DELETE'
        }).then(() => {
            updateMembers()
        })
    }

    useEffect(() => {
        if (members && users && users.length > 0) {
            const memberIds = members.reduce((list, user) => [...list, user.id], []);
            setAvailableUsers(users.filter(user => !memberIds.includes(user.id)));
        }
    }, [members, users]);

    return <div>
        <PageTitle value="Project membership" />
        <div className="heading">
            <Breadcrumb>
                <Link to="/projects">Projects</Link>
                {savedProject && <Link to={`/projects/${savedProject.id}`}>{savedProject.name}</Link>}
            </Breadcrumb>
        </div>

        <Title title='Members' />

        {availableUsers.length > 0 ?
            <form>
                <label>
                    Select user
                    <Select id="userId">
                        {availableUsers && availableUsers.map((user, index) =>
                            <option key={index} value={user.id}>{user.full_name}</option>
                        )}
                    </Select>
                </label>
                <PrimaryButton onClick={handleOnClick} leftIcon={<IconPlus />}>Add as member</PrimaryButton>
            </form> :
            <Alert status="info">
                <AlertIcon />
                All users have been added to the project.
            </Alert>
        }

        <Table>
            <Thead>
                <Tr>
                    <Th style={{ width: '80px' }}>&nbsp;</Th>
                    <Th>Name</Th>
                    <Th>Role</Th>
                    <Th>&nbsp;</Th>
                </Tr>
            </Thead>
            <Tbody>
                {null === members &&
                    <LoadingTableRow numColumns={4} />}
                {null !== members && 0 === members.length &&
                    <NoResultsTableRow numColumns={4} />}
                {members && members.map((member, index) =>
                    <Tr key={index}>
                        <Td><UserAvatar email={member.email} /></Td>
                        <Td><UserLink userId={member.id}>{member.full_name}</UserLink></Td>
                        <Td><UserRoleBadge role={member.role} /></Td>
                        <Td textAlign="right">
                            <DeleteIconButton onClick={() => handleDelete(member)} />
                        </Td>
                    </Tr>
                )
                }
            </Tbody>
        </Table>
    </div>
}