next/router#useRouter JavaScript Examples

The following examples show how to use next/router#useRouter. 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: hiscore-controls.js    From rsc-www with GNU Affero General Public License v3.0 6 votes vote down vote up
function RankSearch(props) {
    const router = useRouter();
    const skill = router.query.skill || 'overall';

    const action = `/hiscores/${skill}`;

    return (
        <HiscoreInputWrap onSubmit={props.onSubmit} action={action}>
            <label htmlFor="search-rank">Search by rank</label>
            <input
                className="rsc-input"
                id="search-rank"
                name="rank"
                type="number"
                min="1"
                required={true}
                defaultValue={props.rank}
            />
        </HiscoreInputWrap>
    );
}
Example #2
Source File: Search.js    From Next.js-e-commerce-online-store with MIT License 6 votes vote down vote up
export default function Search() { 
  const router = useRouter()
  const [ searchValue, setSearchValue ] = useState('')
  const inputRef = useRef(null)

  useEffect(() => {
    if (location.pathname.includes('search')) {
      inputRef.current.value = location.pathname.substr(location.pathname.lastIndexOf('/') + 1)
    } else {
      inputRef.current.value = ''
    }
  }, [])

  const search = e => {
    e.preventDefault()
    if (searchValue !== '') {
      router.push(`/search/${searchValue.trim()}`)
    } else { return null }
  }

  return (
    <form id="formSearchBar" onSubmit={search}>
      <input 
        aria-label="Search graph" 
        type="search" 
        name="search" 
        ref={inputRef} 
        onChange={e => setSearchValue(String(e.target.value))}
      />
      <button type="submit" value="submit" form="formSearchBar" aria-label="Search button">
        <FontAwesomeIcon icon={faSearch} />
      </button>
    </form>
  )
}
Example #3
Source File: _app.jsx    From dogstudio-next-starter with MIT License 6 votes vote down vote up
CustomApp = ({ Component, pageProps }) => {
  /**
   * Router:
   */
  const { locale } = useRouter()

  /**
   * DOM:
   */
  return (
    <>
      <main dir={getLocaleDirection(locale)} className="site-wrapper">
        <Head>
          {/** See: https://github.com/vercel/next.js/blob/master/errors/no-document-viewport-meta.md */}
          <meta
            name="viewport"
            content="width=device-width, initial-scale=1.0, user-scalable=no"
          />
        </Head>

        <Component {...pageProps} />

        <Grid />
      </main>
    </>
  )
}
Example #4
Source File: EventEmbedPage.js    From supportal-frontend with MIT License 6 votes vote down vote up
EventScheduler = () => {
  const [selectedTab, setSelectedTab] = useState('set');
  const { query } = useRouter();

  const handleTabChange = e => {
    setSelectedTab(e.target.value);
  };

  useEffect(() => {
    if (query.zip && !query.event) {
      setSelectedTab(SEARCH);
    }
  }, [query]);

  return (
    <div>
      <TabList
        name="event-method"
        className="mb-4"
        onChange={handleTabChange}
        options={tabOptions}
        value={selectedTab}
      />
      {selectedTab === SET && (
        <MobilizeProvider>
          <SetEventForm />
        </MobilizeProvider>
      )}
      {selectedTab === SEARCH && (
        <MultiMobilizeProvider>
          <SearchEventForm />
        </MultiMobilizeProvider>
      )}
    </div>
  );
}
Example #5
Source File: index.js    From flame-coach-web with MIT License 6 votes vote down vote up
NotFoundPage = () => {
  const [session] = useSession();
  const router = useRouter();

  return (
    <Auth router={router}>
      {session ?
        <DashboardLayout user={session.user}>
          <Page
            title="404 - Page not found"
            isLoading={false}
            isError={false}
          >
            <NotFound
              title="The page you are looking for isn’t here"
              submessage="You either tried some shady route or you came here by mistake. Whichever it is, try using the navigation"
            />
          </Page>
        </DashboardLayout> : null}
    </Auth>
  );
}
Example #6
Source File: [donationId].js    From website with MIT License 6 votes vote down vote up
Donation = ({ donationId, donationDetails, donorDetails, user, prevHref, categoryName }) => {
  const router = useRouter();
  if (donationDetails === undefined || Object.keys(donationDetails).length === 0) {
    return <Error statusCode={404} />;
  }

  return (
    <SessionProvider user={user}>
      <Header title={donationDetails.title} />
      <Head>
        {/* meta property for sharing purposes */}
        <meta property="og:url" content={`https://www.giftforgood.io${router.asPath}`} />
        <meta property="og:type" content="website" />
        <meta property="og:title" content="GiftForGood" />
        <meta property="og:description" content="Check out this donation from GiftForGood!" />
        <meta property="og:image" content={ogImagePath} />
        <meta property="og:image:secure_url" content={ogImagePath} />
        <meta property="og:image:type" content="image/jpeg" />
      </Head>
      <TopNavigationBar showNews={true} />
      <DonationPage
        donationId={donationId}
        donationDetails={donationDetails}
        donorDetails={donorDetails}
        user={user}
        prevHref={prevHref}
        categoryName={categoryName}
      />
      <Footer />
    </SessionProvider>
  );
}
Example #7
Source File: menu.js    From Dose with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
Menu = (props) => {
    const {
        server,
        className
    } = props;
    const router = useRouter();

    useEffect(() => {
        const links = document.getElementsByClassName(Styles.link);
        for (const link of links) {
            if (router.asPath === link.getAttribute('href')) {
                link.classList.add(Styles.active);
            }
        }
    }, []);

    return (
        <nav className={className}>
            <Link href={`/server/${server.server_id}`}><a className={Styles.link}>Home</a></Link>
            <Link href={`/server/${server.server_id}/movies`}><a className={Styles.link}>Movies</a></Link>
            <Link href={`/server/${server.server_id}/shows`}><a className={Styles.link}>TV Shows</a></Link>
        </nav>
    );
}
Example #8
Source File: index.js    From volt-mx-tutorials with Apache License 2.0 6 votes vote down vote up
MpLanding = () => {
  const router = useRouter()

  useEffect(() => {
    // redirect to /hikes route
    const routePath = isDev ? '/hikes' : `/${BASE_PATH_URL}/hikes`
    router.push(routePath, undefined, { shallow: true })
  })
  
  return null;
}
Example #9
Source File: index.js    From origin-dollar with MIT License 6 votes vote down vote up
export default function PoolDetailsPage({ locale, onLocale }) {
  const router = useRouter()
  const { pool_name } = router.query
  const { uniV2OusdUsdt, liquidityOusdUsdt } = useStoreState(
    ContractStore,
    (s) => s.contracts || {}
  )
  const pools = useStoreState(PoolStore, (s) => s.pools)
  const pool = pools.filter((pool) => pool.name === pool_name)[0]

  return (
    process.env.ENABLE_LIQUIDITY_MINING === 'true' && (
      <>
        <Layout onLocale={onLocale} locale={locale} dapp short>
          <Nav dapp page={'pool-details'} locale={locale} onLocale={onLocale} />
          <div className="home d-flex flex-column">
            {pools.length > 0 && <PoolDetails pool={pool} />}
            {pools.length === 0 && <h1>{fbt('Loading...', 'Loading...')}</h1>}
          </div>
        </Layout>
        <style jsx>{`
          .home {
            padding-top: 80px;
          }

          @media (max-width: 799px) {
            .home {
              padding: 0;
            }
          }
        `}</style>
      </>
    )
  )
}
Example #10
Source File: Profile.js    From paras-landing with GNU General Public License v3.0 6 votes vote down vote up
TabProfileMobile = ({ activeTab }) => {
	const router = useRouter()
	const currentUser = useStore((store) => store.currentUser)

	return (
		<div className="mt-4 py-2">
			<ScrollMenu
				LeftArrow={LeftArrow}
				RightArrow={RightArrow}
				wrapperClassName="flex items-center"
				scrollContainerClassName="top-user-scroll"
			>
				{TabItems.filter((item) =>
					router.query.id !== currentUser ? item.title !== 'Liked' : item
				).map((tab) => (
					<TabProfile
						key={tab.title}
						itemId={tab.activeTab}
						title={tab.title}
						linkUrl={tab.linkUrl}
						activeTab={activeTab}
					/>
				))}
			</ScrollMenu>
		</div>
	)
}
Example #11
Source File: index.js    From peppermint with GNU Affero General Public License v3.0 6 votes vote down vote up
export default function ViewNoteBook() {
  const router = useRouter();

  async function getMarkdown() {
    const res = await fetch(`/api/v1/note/${router.query.id}`);
    return res.json();
  }

  const { data, status, error } = useQuery("viewMarkdown", getMarkdown);

  return (
    <div>
      {status === "success" && (
        <>
          <Link href={`/notebook/${data.data.id}/edit`}>
            <button
              type="button"
              className="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md shadow-sm text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500"
            >
              Edit
            </button>
          </Link>

          <div className="mt-4">
            <MD data={data.data.note} />
          </div>
        </>
      )}
    </div>
  );
}
Example #12
Source File: hiscore-controls.js    From rsc-www with GNU Affero General Public License v3.0 5 votes vote down vote up
export default function HiscoreControls() {
    const router = useRouter();

    const skill = router.query.skill || 'overall';
    const rank = +(router.query.rank || 1);
    const username = formatUsername(router.query.name || '');
    const opponent = formatUsername(router.query.opponent || '');

    const onRankSubmit = (e) => {
        e.preventDefault();

        const submittedRank = +e.target[0].value;

        Router.push({
            pathname: `/hiscores/skill/${skill}`,
            query: { rank: submittedRank }
        });

        window.scrollTo(0, 0);
    };

    const onNameSubmit = (e) => {
        e.preventDefault();

        const submittedName = e.target[0].value;

        Router.push({
            pathname: '/hiscores',
            query: { name: submittedName }
        });

        window.scrollTo(0, 0);
    };

    const onCompareSubmit = (e) => {
        e.preventDefault();

        const submittedName = e.target[0].value;
        const opponentName = e.target[1].value;

        Router.push({
            pathname: '/hiscores',
            query: { name: submittedName, opponent: opponentName }
        });

        window.scrollTo(0, 0);
    };

    return (
        <div>
            <div className="rsc-row">
                <div className="rsc-col rsc-col-50">
                    <RankSearch rank={rank} onSubmit={onRankSubmit} />
                </div>
                <div className="rsc-col rsc-col-50">
                    <NameSearch username={username} onSubmit={onNameSubmit} />
                </div>
            </div>
            <br />
            <div className="rsc-row">
                <div className="rsc-col rsc-col-50">
                    <NameCompare
                        username={username}
                        opponent={opponent}
                        onSubmit={onCompareSubmit}
                    />
                </div>
            </div>
        </div>
    );
}
Example #13
Source File: [page].js    From Next.js-e-commerce-online-store with MIT License 5 votes vote down vote up
export default function CategoryPage({ 
  categoryPath, 
  categoryName, 
  lowerCaseCategoryName, 
  categoryItems, 
  totalItems 
}) {  
  const router = useRouter()
  const { page } = router.query

  const paginate = e => router.push({ pathname: `/products/${categoryPath}/${e.selected + 1}`})

  return (
    <>
      <Head>
        <title>{categoryName} - Alimazon</title>
        <meta name="description" content={`The BEST ${lowerCaseCategoryName} in the world!!!`} />
      </Head>

      <DivProducts>
        <nav aria-label="breadcrumb">
          <ol className="breadcrumb">
            <Link href="/"><a className="breadcrumb-item"><li>Home</li></a></Link>
            <li className="breadcrumb-item active" aria-current="page">{categoryName}</li>
          </ol>
        </nav>
        <div className="category-items">
          {categoryItems.map(item => <ProductListItem key={item.id} item={item} />)}
        </div>
        <ReactPaginate 
          forcePage={page - 1}
          pageCount={Math.ceil(totalItems / 8)}
          pageRangeDisplayed={2}
          marginPagesDisplayed={3}
          onPageChange={paginate}
          previousLabel={'«'}
          nextLabel={'»'}
          breakLabel={'...'}
          activeClassName={'active'}
          disableInitialCallback={true}
        />
      </DivProducts>
    </>
  )
}
Example #14
Source File: SearchEventForm.js    From supportal-frontend with MIT License 5 votes vote down vote up
SearchEventForm = () => {
  const [initialValue, setInitialValue] = useState(null);
  const [activeSearchView, setActiveSearchView] = useState(Views.SHIFTS);
  const { hasAttemptedFetch, mobilizeEvents, selectedShifts } = useContext(
    MultiMobilizeContext
  );
  const { query } = useRouter();

  useEffect(() => {
    const { zip, filter, sl } = query;
    if (zip) {
      const newInitialValue = {
        zip,
        filter: filter ? filter.toUpperCase() : 'ALL',
        stagingLocation: !!sl || false,
      };
      setInitialValue(newInitialValue);
    }
  }, [query]);

  const handleSubmit = () => {
    setActiveSearchView(Views.SHIFTS);
    scrollToContentTop();
  };

  return (
    <div>
      <SearchEventFilters initialValue={initialValue} className="mb-8" />
      {hasAttemptedFetch && (
        <>
          <div
            style={{
              display: activeSearchView === Views.SHIFTS ? 'block' : 'none',
            }}
          >
            <SearchEventResults
              onSubmit={() => setActiveSearchView(Views.SIGNUP)}
            />
          </div>
          <div
            style={{
              display: activeSearchView === Views.SIGNUP ? 'block' : 'none',
            }}
          >
            <SelectionSummary
              className="border-b-1 border-t-1 border-grey mb-8 py-4"
              mobilizeEvents={mobilizeEvents}
              shifts={selectedShifts}
              onBack={() => setActiveSearchView(Views.SHIFTS)}
            />
            <EventSignUp shifts={selectedShifts} onSubmit={handleSubmit} />
          </div>
        </>
      )}
    </div>
  );
}
Example #15
Source File: index.js    From flame-coach-web with MIT License 5 votes vote down vote up
TopBar = ({
  className,
  onMobileNavOpen,
  ...rest
}) => {
  const classes = useStyles();

  const router = useRouter();

  return (
    <AppBar
      className={clsx(classes.root, className)}
      elevation={0}
      {...rest}
    >
      <Toolbar>
        <Box flexGrow={1} />
        <Hidden mdDown>
          <IconButton color="inherit" onClick={async () => {
            const response = await signOut({
              callbackUrl: `${process.env.NEXTAUTH_URL}/login`,
              redirect: false
            });

            router.replace(response.url);
          }}>
            <InputIcon />
          </IconButton>
        </Hidden>
        <Hidden lgUp>
          <IconButton
            color="inherit"
            onClick={() => {
              onMobileNavOpen();
            }}
          >
            <MenuIcon />
          </IconButton>
        </Hidden>
      </Toolbar>
    </AppBar>
  );
}
Example #16
Source File: CallToActionButton.js    From website with MIT License 5 votes vote down vote up
CallToActionButton = ({ fullWidth, rounded }) => {
  const user = useUser();
  const router = useRouter();
  const { isDesktop } = useMediaQuery();

  const onButtonClick = () => {
    if (user.npo) {
      if (isDesktop) {
        logDesktopPostWishToAnalytics();
      } else {
        logMobilePostWishToAnalytics();
      }
      router.push('/wishes/create');
    } else if (user.donor) {
      if (isDesktop) {
        logDesktopPostDonationToAnalytics();
      } else {
        logMobilePostDonationToAnalytics();
      }
      router.push('/donations/create');
    }
  };

  if (!user) {
    return null;
  }

  return (
    <Verified>
      {({ isDisabled }) => (
        <Tooltip content={user.donor ? NOT_VERIFIED_MESSAGE_DONOR : NOT_VERIFIED_MESSAGE_NPO} enabled={isDisabled}>
          <Button
            fullWidth={fullWidth}
            asComponent={rounded ? BottomCallToActionButtonStyle : CallToActionButtonStyle}
            size="normal"
            disabled={isDisabled}
            onClick={onButtonClick}
          >
            {user.donor ? 'Donate' : 'Post'}
          </Button>
        </Tooltip>
      )}
    </Verified>
  );
}
Example #17
Source File: [genre].js    From Dose with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
Main = (props) => {
    const router = useRouter();
    const server = props.server;
    const limit = 30;
    const contentServer = new ContentServer(server);
    const { genre } = router.query
    const [offset, setOffset] = useState(0);
    const [gotAllMovies, setGotAllMovies] = useState(false);
    const [fetchingNewData, setFetchingNewData] = useState(false);
    const [movies, setMovies] = useState([]);

    const handleScroll = () => {
        if (!fetchingNewData && !gotAllMovies) {
            fetchData();
        }
    }
    useBottomScrollListener(handleScroll);

    const fetchData = () => {
        setFetchingNewData(true);
        contentServer.getMoviesByGenre(genre, limit, offset).then(data => {
            if (data.length === 0) {
                setGotAllMovies(true);
                setFetchingNewData(false);
                return;
            }
            setOffset(offset + limit);
            setMovies(movies.concat(data));
            setFetchingNewData(false);
        });
    }

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

    const selectMovie = (id) => {
        Router.push(`/server/${server.server_id}/movies/video/${id}`);
    }

    // LAYOUT //
    return (
        <Layout searchEnabled server={server} serverToken={cookie.get('serverToken')} style={{ overflowY: "scroll" }}>
            <Head>
            </Head>
            <div style={{ color: 'white', paddingTop: "120px" }} >
                <div style={{ textAlign: "center" }}>
                    <h1>{genre.charAt(0).toUpperCase() + genre.slice(1)} movies</h1>

                    {movies.map((movie, idx) => {
                        return (
                            <MovieBackdrop multipleRows
                                key={idx}
                                markAsDoneButton
                                id={movie.id}
                                runtime={movie.runtime}
                                title={movie.title}
                                overview={movie.overview}
                                backdrop={movie.backdrop !== null ? `https://image.tmdb.org/t/p/w500/${movie.backdrop}` : 'https://via.placeholder.com/2000x1000'}
                                onClick={(id) => selectMovie(movie.id)} />
                        )
                    })}
                    {fetchingNewData &&
                        <Spinner className={Styles.spinner}></Spinner>
                    }
                </div>
            </div>
        </Layout>
    )
}
Example #18
Source File: ClipfaceLayout.jsx    From clipface with MIT License 5 votes vote down vote up
export function ClipfaceLayout({
  children,
  authInfo = { status: "NOT_AUTHENTICATED" },
  pageName = null,
  pageTitle = null,
  pageSubtitle = null,
}) {
  const router = useRouter();
  const contentClassName = pageName ? `page-${pageName}` : '';

  const onSignOut = () => {
    logout().then((ok) => {
      if (ok) {
        router.push("/login");
      } else {
        alert("Failed to log out, please check your network connection");
      }
    });
  };

  return (
    <>
      <section className="hero is-dark">
        <Header className="hero-head">
          <nav>
            <NavbarContainer>
              <a href="/">
                <h1 className="title is-4">
                  {publicRuntimeConfig.headerTitle}
                </h1>
              </a>
              <NavbarMenu>
                {authInfo.status == "AUTHENTICATED" && (
                  <a onClick={onSignOut}>
                    Log out
                  </a>
                )}
              </NavbarMenu>
            </NavbarContainer>
          </nav>
        </Header>

        {pageTitle && (
          <div className="hero-body">
            <div className="container has-text-centered">
              <p className="title">{pageTitle}</p>

              {pageSubtitle && <p className="subtitle">{subtitle}</p>}
            </div>
          </div>
        )}
      </section>

      <ApplicationDiv className={contentClassName}>
        {children}
      </ApplicationDiv>

      <Footer>
        <div className="container">
          <a href="https://github.com/Hubro/clipface" target="_blank">
            <i class="fab fa-github"></i> Clipface
          </a>
        </div>
      </Footer>
    </>
  );
}
Example #19
Source File: Layout.js    From nextjs-starter-blog with MIT License 5 votes vote down vote up
Header = () => {
  const { setTheme, resolvedTheme } = useTheme();
  const { pathname } = useRouter();
  const [mounted, setMounted] = useState(false);

  useEffect(() => setMounted(true), []);

  const toggleDarkMode = (checked) => {
    const isDarkMode = checked;

    if (isDarkMode) setTheme("dark");
    else setTheme("light");
  };

  const isRoot = pathname === "/";
  const isDarkMode = resolvedTheme === "dark";

  return (
    <header
      className={clsx("flex items-center justify-between ", {
        "mb-8": isRoot,
        "mb-2": !isRoot,
      })}
    >
      <div className={"max-w-md"}>
        {isRoot ? <LargeTitle /> : <SmallTitle />}
      </div>
      {mounted && (
        <div className="flex space-x-4">
          <DarkModeSwitch checked={isDarkMode} onChange={toggleDarkMode} />
          <Link href="/rss.xml" passHref>
            <a target="__blank" rel="noreferrer noopener">
              <RSSIcon className={isDarkMode ? "text-white" : "text-black"} />
            </a>
          </Link>
        </div>
      )}
    </header>
  );
}
Example #20
Source File: Product.js    From amazon-next with MIT License 5 votes vote down vote up
export default function Product({ product }) {
    const router = useRouter();
    const newProductData = { tags: [], ...product };

    const stars = useMemo(() => {
        const initial = [];
        for (let i = 1; i < product.stars; i += 1) {
            initial.push(
                <FontAwesomeIcon
                    key={i}
                    size="lg"
                    className="w-5"
                    icon={faStar}
                    color="#e69d3f"
                />
            );
        }

        return initial;
    }, [product.stars]);

    function handleClick() {
        return router.push({
            pathname: '/details',
            query: { productId: product.id },
        });
    }

    return (
        <div className="h-180 flex flex-row lg:mr-8 mr-0 rounded-lg max-w-500">
            <div className="flex items-center">
                <img
                    src={newProductData.image}
                    alt={newProductData.name}
                    aria-label={newProductData.name}
                    className="w-140 max-h-200"
                />
            </div>

            <div className="flex flex-col justify-between ml-3">
                <strong className="max-w-240 truncate text-xl text-gray-800">
                    {newProductData.name}
                </strong>

                <div className="flex flex-row">
                    <div>
                        <span className="font-bold text-gray-800">
                            ${product.price / 100}
                        </span>
                    </div>
                </div>
                <div className="flex flex-row">{stars}</div>

                <Button
                    handleClick={handleClick}
                    title="Click to see more details about this product"
                >
                    {' '}
                    More details{' '}
                </Button>
            </div>
        </div>
    );
}
Example #21
Source File: SingleApp.js    From winstall with GNU General Public License v3.0 5 votes vote down vote up
Description = ({ desc, id, full }) => {
  const [descTrimmed, setDescTrimmed] = useState(desc.length > 140);
  const router = useRouter();

  let toggleDescription = (e, status) => {
    e.stopPropagation();

    if(desc.length > 340){
      router.push('/apps/[id]', `/apps/${id}`)
      return;
    };

    setDescTrimmed(status);
  };

  if(!desc) return <p>No description available for this app.</p>


  return (
    <>
      <p>
        {desc.length > 140
          ? !descTrimmed || full
            ? desc
            : `${desc.substr(0, 140).replace(/(^[\s]+|[\s]+$)/g, "")}...`
          : desc}
      </p>

      {desc.length > 140 && !full && (
        <button
          onClick={(e) => toggleDescription(e, !descTrimmed)}
          className={styles.subtle}
        >
          {descTrimmed ? (
            <>
              Full Description
              <FiChevronDown />
            </>
          ) : (
            <>
              Hide Description
              <FiChevronUp />
            </>
          )}
        </button>
      )}
    </>
  );
}
Example #22
Source File: nav-link.js    From Nextjs-ja-translation-docs with MIT License 5 votes vote down vote up
function NavLink({ route: { href, pathname, title, selected }, onClick }) {
  const router = useRouter();
  const onlyHashChange = pathname === router.pathname;

  return (
    <div className={cn('nav-link', { selected })}>
      {
        // NOTE: use just anchor element for triggering `hashchange` event
        onlyHashChange ? (
          <a className={selected ? 'selected' : ''} href={pathname}>
            {title}
          </a>
        ) : (
          <Link href={href} as={pathname}>
            <a onClick={onClick}>{title}</a>
          </Link>
        )
      }
      <style jsx>{`
        div.selected {
          box-sizing: border-box;
        }
        .nav-link {
          display: flex;
        }
        .nav-link :global(a) {
          text-decoration: none;
          font-size: 1rem;
          line-height: 1.5rem;
          color: #444444;
          box-sizing: border-box;
        }
        .selected :global(a) {
          font-weight: 600;
          color: #000;
        }
        .nav-link:hover :global(a) {
          color: #000;
        }
        span {
          color: #979797;
        }
        @media screen and (max-width: 950px) {
          div {
            padding-top: 0;
            padding-left: 0;
            padding-bottom: 0;
          }
          div.selected {
            border-left: none;
            padding-left: 0;
          }
          .nav-link :global(a) {
            display: flex;
            align-items: center;
          }
        }
      `}</style>
    </div>
  );
}
Example #23
Source File: [id].js    From Simplify-Testing-with-React-Testing-Library with MIT License 5 votes vote down vote up
export default function Post() {
  const router = useRouter()
  const { data, error } = useSwr(
    router.query.id ? `/api/post/${router.query.id}` : null,
    fetcher
  )

  if (error)
    return (
      <>
        <CustomHead title="failed loading blog" />
        <div>Failed to load blog post</div>
      </>
    )
  if (!data)
    return (
      <>
        <CustomHead title="Loading..." />
        <div>Loading...</div>
      </>
    )

  const handleDelete = async e => {
    e.preventDefault()
    const res = await fetch(`/api/delete/${data.post.id}`, {
      method: 'DELETE',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ id: data.post.id, title: data.post.title })
    })

    if (res.status === 200) {
      Router.push('/')
    }
  }

  return (
    <div>
      <CustomHead title={data.post.title} />
      <div className="md:max-w-3xl mx-auto">
        <div className="w-full px-4 md:px-6 text-xl text-gray-800 leading-normal">
          <div>
            <div className="flex justify-between">
              <Link href="/">
                <p className="text-base md:text-sm text-green-500 font-bold cursor-pointer">
                  &lt;
                  <a className="text-base md:text-sm text-green-500 font-bold no-underline hover:underline">
                    BACK TO BLOG
                  </a>
                </p>
              </Link>

              <a
                onClick={handleDelete}
                className="cursor-pointer font-bold text-base text-red-700 hover:underline"
              >
                Delete post&#62;
              </a>
            </div>
            <h1 className="font-bold font-sans break-normal text-gray-900 pt-6 pb-2 text-3xl md:text-4xl">
              {data.post.title}
            </h1>
            <p className="text-sm md:text-base font-normal text-gray-600">
              Published {data.post.created}
            </p>
            <img
              className="rounded"
              src={data.post.image_url}
              alt={data.post.title}
            />
          </div>
          <p className="py-6">{data.post.content}</p>
        </div>
      </div>
    </div>
  )
}
Example #24
Source File: TopTransactionCard.js    From paras-landing with GNU General Public License v3.0 5 votes vote down vote up
TopTransactionCard = ({ contract_token_id, setLocalToken }) => {
	const router = useRouter()
	const [token, setToken] = useState(null)

	const [contractId, tokenId] = contract_token_id.split('::')

	useEffect(() => {
		fetchData()
	}, [])

	const fetchData = async () => {
		const params = {
			contract_id: contractId,
			token_id: tokenId,
		}
		const resp = await Cachios.get(`${process.env.V2_API_URL}/token`, {
			params: params,
			ttl: 60,
		})
		setToken(resp.data.data.results[0])
	}

	if (!token) {
		return null
	}

	return (
		<div
			id={contract_token_id}
			className="w-1/3 md:w-1/5 px-2 inline-block whitespace-normal overflow-visible flex-shrink-0"
		>
			<div className="w-full m-auto" onClick={() => setLocalToken(token)}>
				<Card
					imgUrl={parseImgUrl(token.metadata.media, null, {
						width: `200`,
						useOriginal: process.env.APP_ENV === 'production' ? false : true,
						isMediaCdn: token.isMediaCdn,
					})}
					onClick={() => {
						router.push(
							{
								pathname: router.pathname,
								query: {
									...router.query,
									tokenId: token.token_id,
									contractId: token.contract_id,
								},
							},
							`/token/${token.contract_id}::${token.token_series_id}/${token.token_id}`,
							{
								shallow: true,
								scroll: false,
							}
						)
					}}
					imgBlur={token.metadata.blurhash}
					token={{
						title: token.metadata.title,
						collection: token.metadata.collection || token.contract_id,
						copies: token.metadata.copies,
						creatorId: token.metadata.creator_id || token.contract_id,
						is_creator: token.is_creator,
					}}
				/>
			</div>
		</div>
	)
}
Example #25
Source File: _app.js    From peppermint with GNU Affero General Public License v3.0 5 votes vote down vote up
function MyApp({ Component, pageProps: { session, ...pageProps } }) {
  const router = useRouter();

  if (router.asPath.slice(0, 5) === "/auth") {
    return (
      <SessionProvider session={session}>
        <Component {...pageProps} />
      </SessionProvider>
    );
  }

  return (
    <>
     <Head>
        <meta charSet="utf-8" />
        <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
        <meta
          name="viewport"
          content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"
        />
        <meta name="description" content="Ticket management system selfhosted open source" />
        <meta name="keywords" content="Keywords" />
        <title>Peppermint</title>

        {/* <link rel="manifest" href="/manifest.json" /> */}
        
        <link
          href="/favicon/favicon.ico"
          rel="icon"
        />
        <link
          href="/favicon/favicon-16x16.png"
          rel="icon"
          type="image/png"
          sizes="16x16"
        />
        <link
          href="/favicon/favicon-32x32.png"
          rel="icon"
          type="image/png"
          sizes="32x32"
        />
        <link rel="apple-touch-icon" href="/apple-icon.png"></link>
        <meta name="theme-color" content="#317EFB" />
      </Head>
      <SessionProvider session={session}>
        <QueryClientProvider client={queryClient}>
          <Auth>
            <SideLayout>
              <Component {...pageProps} />
            </SideLayout>
          </Auth>
        </QueryClientProvider>
      </SessionProvider>
    </>
  );
}
Example #26
Source File: login.js    From rsc-www with GNU Affero General Public License v3.0 4 votes vote down vote up
export default function Login(props) {
    const { token } = useContext(SessionContext);
    const router = useRouter();
    const redirect = router.query.to;

    const askLogin = redirect ? (
        <p>
            <strong className="rsc-caution-text">
                You need to login to access this feature
            </strong>
        </p>
    ) : undefined;

    const message = props.errorMessage ? (
        <p>
            <strong className="rsc-warning-text">{props.errorMessage}</strong>
        </p>
    ) : undefined;

    const onSubmit = (event) => {
        setTimeout(() => {
            event.target.querySelector('input[type="submit"]').disabled = true;
            document.body.style.cursor = 'busy';
        }, 2);
    };

    return (
        <div>
            <Header pageName={PAGE_TITLE} />
            <Container>
                <PageName pageName={PAGE_TITLE} />
                <div className="rsc-row">
                    <div className="rsc-col rsc-col-100">
                        <div className="rsc-box">
                            {askLogin}
                            <p>
                                Never enter your password anywhere except
                                2003scape.com
                            </p>
                            <img
                                className="rsc-image"
                                src="/only-2003scape.gif"
                                alt="Only enter your password on 2003scape.com"
                            />
                            {message}
                            <div className="rsc-form rsc-login-form">
                                <form
                                    action="/login"
                                    method="post"
                                    onSubmit={onSubmit}
                                >
                                    <input
                                        type="hidden"
                                        name="_csrf"
                                        value={token}
                                    />
                                    <input
                                        type="hidden"
                                        name="redirect"
                                        value={redirect}
                                    />
                                    <div className="rsc-row">
                                        <label
                                            htmlFor="username"
                                            className="rsc-col rsc-col-50 rsc-form-label"
                                        >
                                            2003Scape Username:
                                        </label>
                                        <div className="rsc-col rsc-col-50">
                                            <UsernameInput
                                                name="username"
                                                id="username"
                                            />
                                        </div>
                                    </div>
                                    <div className="rsc-row">
                                        <label
                                            htmlFor="password"
                                            className="rsc-col rsc-col-50 rsc-form-label"
                                        >
                                            2003Scape Password:
                                        </label>
                                        <div className="rsc-col rsc-col-50">
                                            <input
                                                className="rsc-input"
                                                name="password"
                                                id="password"
                                                type="password"
                                                maxLength="20"
                                                minLength="3"
                                                required
                                            />
                                        </div>
                                    </div>
                                    <div className="rsc-row">
                                        <label
                                            htmlFor="remember"
                                            className="rsc-col rsc-col-50 rsc-form-label"
                                        >
                                            Remember me:
                                        </label>
                                        <div className="rsc-col rsc-col-50">
                                            <input
                                                className="rsc-input"
                                                name="remember"
                                                id="remember"
                                                type="checkbox"
                                            />
                                        </div>
                                    </div>
                                    <input
                                        className="rsc-input"
                                        type="submit"
                                        value="Secure Login"
                                    />
                                </form>
                                <br />
                                <div className="rsc-row">
                                    <div className="rsc-col rsc-col-50">
                                        <div className="rsc-stone-box">
                                            <Link href="#">
                                                <a className="rsc-link">
                                                    <h2>Lost password?</h2>
                                                </a>
                                            </Link>
                                            If you have lost/forgotten your
                                            password or need to recover your
                                            account.
                                        </div>
                                    </div>
                                    <div className="rsc-col rsc-col-50">
                                        <div className="rsc-stone-box">
                                            <Link href="/manual/about/getting-started">
                                                <a className="rsc-link">
                                                    <h2>Need an account?</h2>
                                                </a>
                                            </Link>
                                            Create a RuneScape account to access
                                            our game and secure services.
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </Container>
        </div>
    );
}
Example #27
Source File: index.js    From lullaby with GNU General Public License v3.0 4 votes vote down vote up
function Treatment({ t }) {
  const DAY_NAMES = [
    t("Sun"),
    t("Mon"),
    t("Tue"),
    t("Wed"),
    t("Thu"),
    t("Fri"),
    t("Sat")
  ];

  const router = useRouter();

  const urlFrequency = !isNaN(parseInt(router.query.frequency))
    ? parseInt(router.query.frequency)
    : null;

  const [sessionStartedAt, setSessionStartedAt] = useState(null);
  const [currentTime, setCurrentTime] = useState(Date.now());
  const [statsByDay, setStatsByDay] = useState(null);
  const [isPlaying, setIsPlaying] = useState(false);

  const [state, setState] = useState({
    isVisible: false,
    isAnimating: false,
    frequency: urlFrequency,
    doTutorial: false
  });
  const stateForCallbacks = useRef(state);

  useEffect(() => {
    stateForCallbacks.current = {
      isPlaying: !!isPlaying,
      sessionStartedAt: parseInt(sessionStartedAt)
    };
  }, [isPlaying, sessionStartedAt]);

  // Run a clock to update the timer
  useEffect(() => {
    // TODO: Do a proper server-side redirect
    if (
      urlFrequency &&
      (parseInt(router.query.frequency) < 0 ||
        parseInt(router.query.frequency) > 20000)
    ) {
      Router.replace("/treatment");
    }

    const startDate = new Date();
    startDate.setDate(startDate.getDate() - NUM_DAYS_SHOW);
    const endDate = new Date();
    endDate.setDate(endDate.getDate() + NUM_DAYS_SHOW);
    setStatsByDay(User.getSessionDurationsByDay(startDate, endDate));

    setSessionStartedAt(0);
    const timer = setInterval(() => setCurrentTime(Date.now()), 1000);

    setState({ ...state, frequency: urlFrequency || User.getFrequency() });

    return () => clearInterval(timer);
  }, []);

  const playToggle = () => {
    if (!tinnitusPlayer) {
      tinnitusPlayer = new Tinnitus(
        state.frequency || urlFrequency || User.getFrequency() || 0,
        Math.pow(1.2, User.getVolume() || 0.1) - 1.0
      );
    }

    if (!isPlaying) {
      // Play
      tinnitusPlayer.play();
      tinnitusPlayer.setVolume(Math.pow(1.2, User.getVolume() || 0.1) - 1.0);

      setSessionStartedAt(currentTime || Date.now());
      setIsPlaying(true);

      User.trackSessionStart(
        state.frequency || urlFrequency || User.getFrequency()
      );
    } else {
      // Stop
      tinnitusPlayer.stop();

      setIsPlaying(false);

      if (sessionStartedAt) {
        User.addSession(
          state.frequency || urlFrequency || User.getFrequency(),
          sessionStartedAt,
          Date.now()
        );
        User.trackSessionStop(Math.floor(Date.now() - sessionStartedAt) / 1000);
        setSessionStartedAt(0);
      }
    }
  };

  useEffect(() => {
    User.init();

    setState({ ...state, doTutorial: !User.getDidTutorial() });

    if (window) {
      // If the window is closed record the session anyway (same as unmount)
      window.addEventListener("beforeunload", handleUnmount);
    }

    doFadeIn();

    // Unmount
    return handleUnmount;
  }, []);

  const handleUnmount = () => {
    if (tinnitusPlayer) tinnitusPlayer.stop();
    const { isPlaying, sessionStartedAt } = stateForCallbacks.current;

    // Log the session before quitting
    if (isPlaying && sessionStartedAt && User.getFrequency()) {
      User.addSession(User.getFrequency(), sessionStartedAt, Date.now());
      User.trackSessionStop(
        User.getFrequency(),
        Math.floor(Date.now() - sessionStartedAt) / 1000
      );
    }

    if (window) {
      window.removeEventListener("beforeunload", handleUnmount);
    }
  };

  const resetFrequency = () => {
    User.setFrequency("");
    setState({ ...state, frequency: "" });
  };

  const handleSetFrequency = newFrequency => {
    User.setFrequency(newFrequency);
    setState({ ...state, frequency: newFrequency });
  };

  const onChangeVolume = volume => {
    User.setVolume(volume);

    if (tinnitusPlayer) {
      tinnitusPlayer.setVolume(Math.pow(1.2, volume) - 1.0);
    }
  };

  const startTutorial = e => {
    e.preventDefault();
    if (isPlaying) playToggle();
    setState({ ...state, doTutorial: true });
  };

  const endTutorial = volume => {
    User.setDidTutorial();
    User.setVolume(volume);
    setState({ ...state, doTutorial: false });
  };

  const doFadeIn = async () => {
    // Fade In on start
    setState({ ...state, isVisible: true, isAnimating: true });
    await sleep(FADE_DURATION);
    setState({ ...state, isVisible: true, isAnimating: false });
  };

  const { isVisible, doTutorial } = state;
  const frequency = urlFrequency || User.getFrequency();
  const userVolume = User.getVolume() || 0.1;
  const timeElapsed =
    (sessionStartedAt
      ? Math.floor((currentTime - sessionStartedAt) / 1000)
      : 0) + User.getTodayDuration();

  return (
    <div className="Treatment">
      <Head>
        <title>
          {frequency
            ? t(`TreatmentTitle`).replace(`{frequency}`, frequency)
            : t(`TreatmentTitleSimple`)}
        </title>
      </Head>

      <TopNavTreatment
        title={
          frequency
            ? t(`TreatmentHeader`).replace(`{frequency}`, frequency)
            : "TreatmentHeaderEmpty"
        }
        showUserFrequency={
          frequency != User.getFrequency() ? User.getFrequency() : false
        }
        showDiagnosticReminder={
          !!User.getFrequency() && User.hasOldDiagnostic() === true
        }
        buttonText={t(`ReadInstructions`)}
        buttonHref={`/instructions`}
      />

      <VisibilityAnimation pose={isVisible ? "visible" : "hidden"}>
        {!frequency ? (
          <TreatmentInput onSubmit={handleSetFrequency} />
        ) : doTutorial ? (
          <VolumeTutorial onFinish={endTutorial} />
        ) : (
          <div className="playerHolder">
            <div className="stats">
              {!!statsByDay &&
                statsByDay.map(d => (
                  <DayProgress
                    key={`day-${d.day}`}
                    dayName={t(DAY_NAMES[d.day])}
                    progress={d.duration / TARGET_SECONDS}
                    highlight={d.day == new Date().getDay()}
                  />
                ))}
            </div>

            <div className="player">
              <div className="play">
                <TreatmentButton
                  onClick={playToggle}
                  isPlaying={isPlaying}
                  currentDuration={timeElapsed}
                  targetDuration={TARGET_SECONDS}
                />
              </div>
            </div>
            <div className="volume">
              <VolumeControl
                min={0.001}
                defaultValue={userVolume || 0.001}
                max={1.0}
                step={0.001}
                onChange={onChangeVolume}
              />
            </div>
            <div className="diagnostic">
              <div>
                {t(`ForgotVolume`)}{" "}
                <a onClick={startTutorial}>{t(`DoTutorial`)}</a>
              </div>
              <div>
                {t(`ForgotFrequency`)}{" "}
                <Link href="/diagnostic">
                  <a>{t(`DoDiagnostic`)}</a>
                </Link>
                {!urlFrequency && (
                  <>
                    {" "}
                    {t(`or`)}{" "}
                    <a onClick={resetFrequency}>{t(`SetFrequency`)}</a>
                  </>
                )}
              </div>
            </div>
          </div>
        )}
      </VisibilityAnimation>

      {/*language=SCSS*/}
      <style jsx>{`
        .Treatment {
          box-sizing: border-box;
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          min-height: 100%;
          padding: 170px 20px 100px 20px;
          text-align: center;
          z-index: 0;
        }
        .Treatment :global(> div) {
          // This is the animation holder
          width: 100%;
        }
        @media (max-width: 768px) {
          :global(.Treatment) {
            padding-top: 180px;
          }
        }
        .stats {
          max-width: 30em;
          margin: 0 auto;
          display: grid;
          grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
          grid-gap: 1px;
          margin-bottom: 30px;
        }
        .diagnostic {
          font-size: 14px;
          color: var(--textLight);
          margin-top: 4em;
        }
        .diagnostic a {
          text-decoration: underline;
          cursor: pointer;
          color: var(--textLight);
        }
        .diagnostic > div {
          margin-bottom: 0.5em;
        }
        .playerHolder {
          max-width: 600px;
          margin: 0 auto;
        }
        .player {
          padding: 3em 0;
        }
        @media (max-width: 480px) {
          .player {
            grid-template-columns: 1fr;
          }
          .player .timer {
            text-align: center;
          }
        }
        .timer .intro {
          font-size: 16px;
          color: #b7b5db;
        }
        .timer .timeLeft {
          font-size: 30px;
          font-weight: bold;
          line-height: 1.1;
          color: #0e87ff;
        }
        .volume {
          max-width: 550px;
          margin: 0 auto;
          margin-top: 30px;
        }

        @media (max-height: 700px) {
          .stats {
            font-size: 14px;
          }
          .player {
            padding: 0.5em 0;
          }
          .diagnostic {
            margin-top: 2em;
          }
        }
      `}</style>
    </div>
  );
}
Example #28
Source File: Layout.js    From Next.js-e-commerce-online-store with MIT License 4 votes vote down vote up
export default function Layout(props) {
  const [ isAuthModalVisible, setIsAuthModalVisible ] = useState(false)
  const [ isLogInTabVisible, setIsLogInTabVisible ] = useState(null)  
  const [ isCookieBannerVisible, setIsCookieBannerVisible ] = useState(false)

  const { areCookiesAccepted, setAreCookiesAccepted } = useContext(CookiesContext)

  useEffect(() => {
    if (localStorage.getItem('areCookiesAccepted') !== null) {
      if (localStorage.getItem('areCookiesAccepted') === 'false') {
        setAreCookiesAccepted(false)
      } else {
        setAreCookiesAccepted(true)
      }
    }
  }, [isCookieBannerVisible])

  useEffect(() => {
    if (
      localStorage.getItem('isCookieBannerVisible') !== null ||
      localStorage.getItem('isCookieBannerVisible') === 'false'
    ) {
      setIsCookieBannerVisible(false)
    } else {
      setIsCookieBannerVisible(true)
    }
  }, [areCookiesAccepted])

  const router = useRouter()
  useEffect(() => {
    if (localStorage.getItem('areCookiesAccepted') === 'true') {
      const handleRouteChange = url => {
        gtag.pageview(url)
      }
      router.events.on('routeChangeComplete', handleRouteChange)
      return () => {
        router.events.off('routeChangeComplete', handleRouteChange)
      }
    } else if (localStorage.getItem('areCookiesAccepted') === 'false') {
      document.cookie = '_ga=; Max-Age=0;'

      const cookiePair = document.cookie.split('; ').find(row => row.startsWith('_ga_'))
      if (cookiePair !== undefined) {
        const cookieName = cookiePair.substring(0, cookiePair.indexOf('='))
        document.cookie = `${cookieName}=; Max-Age=0;`
      }
      
      document.cookie = '_gid=; Max-Age=0;'
    }
  }, [router.events, areCookiesAccepted])

  const handleVisibility = e => {
    if (e.currentTarget.name === 'logIn') {
      setIsAuthModalVisible(true)
      setIsLogInTabVisible(true)
      document.body.style.overflow = 'hidden'
    } else if (e.currentTarget.name === 'singUp') {
      setIsAuthModalVisible(true)
      setIsLogInTabVisible(false)
      document.body.style.overflow = 'hidden'
    }
  }

  const closeAuthModal = e => {
    if (e.target.id === 'outsideAuthModal') {
      setIsAuthModalVisible(false)
      document.body.style.overflow = 'visible'
    }
  }

  const showCookieBanner = e => {
    e.preventDefault()
    setIsCookieBannerVisible(true)
  }

  return (
    <>
      <GlobalStyle />
      <DivGrid>
        <Header handleVisibility={handleVisibility} />
        {
          isAuthModalVisible
          ? <AuthForm 
              isLogInTabVisible={isLogInTabVisible}
              closeAuthModal={closeAuthModal}
              handleVisibility={handleVisibility} 
            />
          : null
        }
        {props.children}
        <CookieBanner 
          isCookieBannerVisible={isCookieBannerVisible}
          setAreCookiesAccepted={setAreCookiesAccepted}
          setIsCookieBannerVisible={setIsCookieBannerVisible}
        />
        <Footer showCookieBanner={showCookieBanner} />
      </DivGrid>
    </>
  )
}
Example #29
Source File: create.js    From quadratic-voting with GNU Affero General Public License v3.0 4 votes vote down vote up
export default function Create() {
  // Router object
  const router = useRouter();
  // Global settings object
  const [globalSettings, setGlobalSettings] = useState(defaultGlobalSettings);
  // Current subject object
  const [currentSubject, setCurrentSubject] = useState(defaultCurrentSubject);
  // Array of all subjects
  const [subjects, setSubjects] = useState([]);
  // Loading state
  const [loading, setLoading] = useState(false);

  /**
   * Sets the number of voters (between 1 - 250)
   * @param {number} value number of voters
   */
  const setNumVoters = (value) => {
    setGlobalSettings({
      ...globalSettings, // Current settings
      num_voters: Math.max(1, Math.min(250, Number(Math.round(value)))), // Number between 1 - 250 and not decimal
    });
  };

  /**
   * Sets the number of voting credits per voter (min. 1)
   * @param {number} value number of voting credits
   */
  const setCreditsPerVoter = (value) => {
    setGlobalSettings({
      ...globalSettings, // Current settings
      credits_per_voter: Math.max(1, Number(Math.round(value))), // Number above 1 and not decimal
    });
  };

  /**
   * Sets event start/end date
   * @param {string} type name of object date key
   * @param {object} value moment date object
   */
  const setEventData = (type, value) => {
    setGlobalSettings({
      ...globalSettings,
      [type]: value,
    });
  };

  /**
   * Updates subject object with input field information
   * @param {string} field object key
   * @param {string} value input field value
   */
  const setSubjectData = (field, value) => {
    setCurrentSubject({
      ...currentSubject,
      [field]: value,
    });
  };

  /**
   * Submits subject to array
   */
  const submitSubject = () => {
    // Push subject to subjects array
    setSubjects((oldSubjects) => [...oldSubjects, currentSubject]);
    // Clear current subject by resetting to default
    setCurrentSubject(defaultCurrentSubject);
  };

  /**
   * Edits item with x index by setting it to current and deleting from subjects[]
   * @param {number} index array index of item to edit
   */
  const editSubject = (index) => {
    // Set current subject to to-be-edited item
    setCurrentSubject(subjects[index]);
    // Delete to-be-edited item from subjects array
    deleteSubject(index);
  };

  /**
   * Deletes item with x index by filtering it out of subjects[]
   * @param {number} index array index of item to delete
   */
  const deleteSubject = (index) => {
    // Filter array for all items that are not subjects[index]
    setSubjects(subjects.filter((item, i) => i !== index));
  };

  /**
   * POST event creation endpoint
   */
  const submitEvent = async () => {
    // Toggle loading
    setLoading(true);

    // Post create endpoint and retrieve event details
    const eventDetails = await axios.post("/api/events/create", {
      ...globalSettings,
      subjects,
    });

    // Toggle loading
    setLoading(false);

    // Redirect to events page on submission
    router
      .push(
        `/event?id=${eventDetails.data.id}&secret=${eventDetails.data.secret_key}`
      )
      .then(() => window.scrollTo(0, 0));
  };

  return (
    <Layout>
      {/* Navigation header */}
      <Navigation
        history={{
          title: "Home",
          link: "/",
        }}
        title="Create Event"
      />

      {/* Create page */}
      <div className="create">
        {/* Create page heading */}
        <div className="create__content">
          <h1>Create a new event</h1>
          <p>
            To create an event, simply fill out the event settings, add your
            options, and we will generate you quicklinks that you can share with
            your audience.
          </p>
        </div>

        {/* Global settings */}
        <div className="create__settings">
          {/* Global settings header */}
          <h2>Global Settings</h2>
          <p>
            These settings are used to setup your event. You can add an event
            title and description, select the number of voters, how many vote
            credits they'll each receive, and a start and end date for voting.
          </p>

          {/* Event title selection */}
          <div className="create__settings_section">
            <label htmlFor="event_title">Event title</label>
            <p>What is your event called?</p>
            <input
              type="text"
              id="event_title"
              value={globalSettings.event_title}
              onChange={(e) => setEventData("event_title", e.target.value)}
            />
          </div>

          {/* Event description selection */}
          <div className="create__settings_section">
            <label htmlFor="event_description">Event description</label>
            <p>Describe your event in under 240 characters:</p>
            <input
              type="text"
              id="event_description"
              value={globalSettings.event_description}
              maxLength="240"
              onChange={(e) =>
                setEventData("event_description", e.target.value)
              }
            />
          </div>

          {/* Number of voters selection */}
          <div className="create__settings_section">
            <label htmlFor="num_voters">Number of voters</label>
            <p>How many voting links would you like to generate? (Max: 250)</p>
            <input
              type="number"
              id="num_voters"
              value={globalSettings.num_voters}
              onChange={(e) => setNumVoters(e.target.value)}
            />
          </div>

          {/* Number of credits per voter selection */}
          <div className="create__settings_section">
            <label htmlFor="credits_per_voter">Vote credits per voter</label>
            <p>How many votes will each voter receive?</p>
            <input
              type="number"
              max="100"
              min="1"
              step="1"
              id="credits_per_voter"
              value={globalSettings.credits_per_voter}
              onChange={(e) => setCreditsPerVoter(e.target.value)}
            />
          </div>

          {/* Event start date selection */}
          <div className="create__settings_section">
            <label>Event start date</label>
            <p>When would you like to begin polling?</p>
            <Datetime
              className="create__settings_datetime"
              value={globalSettings.event_start_date}
              onChange={(value) => setEventData("event_start_date", value)}
            />
          </div>

          {/* Event end date selection */}
          <div className="create__settings_section">
            <label>Event end date</label>
            <p>When would you like to end polling?</p>
            <Datetime
              className="create__settings_datetime"
              value={globalSettings.event_end_date}
              onChange={(value) => setEventData("event_end_date", value)}
            />
          </div>
        </div>

        {/* Subject settings */}
        <div className="create__settings">
          {/* Subject settings heading */}
          <h2>Options</h2>
          <p>
            These settings enable you to add options that voters can delegate
            their voting credits to. You can choose to add an option title,
            description, and link.
          </p>

          {/* Listing of all subjects via accordion*/}
          <h3>Options</h3>
          <div className="create__settings_section">
            {subjects.length > 0 ? (
              // If subjects array contains at least one subject
              <Accordion>
                {subjects.map((subject, i) => {
                  // Render subjects in accordion
                  return (
                    <AccordionItem key={i}>
                      <AccordionItemHeading>
                        <AccordionItemButton>
                          {subject.title}
                        </AccordionItemButton>
                      </AccordionItemHeading>
                      <AccordionItemPanel>
                        {subject.description !== "" ? (
                          // If subject has a description
                          <div className="accordion__value">
                            <label>Description</label>
                            <textarea value={subject.description} disabled />
                          </div>
                        ) : null}
                        {subject.url !== "" ? (
                          // If subject has a URL
                          <div className="accordion__value">
                            <label>Link</label>
                            <a
                              href={subject.url}
                              target="_blank"
                              rel="noopener noreferrer"
                            >
                              {subject.url}
                            </a>
                          </div>
                        ) : null}
                        <div className="accordion__buttons">
                          <button onClick={() => editSubject(i)}>
                            Edit Option
                          </button>
                          <button onClick={() => deleteSubject(i)}>
                            Delete Option
                          </button>
                        </div>
                      </AccordionItemPanel>
                    </AccordionItem>
                  );
                })}
              </Accordion>
            ) : (
              // Else, if no subjects in subjects array
              <span className="empty__subjects">No options added</span>
            )}
          </div>

          {/* Form to add subjects */}
          <h3>Add Options</h3>
          <div className="create__settings_section">
            {/* Subject addition form */}
            <div className="create__subject_form">
              {/* Add subject tile */}
              <div>
                <label>Option Title</label>
                <input
                  type="text"
                  placeholder="My Option Title"
                  value={currentSubject.title}
                  onChange={(e) => setSubjectData("title", e.target.value)}
                />
              </div>

              {/* Add subject description */}
              <div>
                <label>Option Description</label>
                <textarea
                  placeholder="Description of the option."
                  value={currentSubject.description}
                  onChange={(e) =>
                    setSubjectData("description", e.target.value)
                  }
                />
              </div>

              {/* Add subject link */}
              <div>
                <label>Option Link</label>
                <input
                  type="text"
                  placeholder="www.council.org/vote_info/1"
                  value={currentSubject.url}
                  onChange={(e) => setSubjectData("url", e.target.value)}
                />
              </div>

              {currentSubject.title !== "" ? (
                // If form has title filled, allow submission
                <button onClick={submitSubject}>Add Option</button>
              ) : (
                // Else, show disabled state
                <button className="button__disabled" disabled>
                  Enter Title
                </button>
              )}
            </div>
          </div>
        </div>

        {/* Submit event creation */}
        <div className="create__submission">
          {subjects.length > 1 ? (
            // If subjects have been provided, enable event creation
            <button className="create__event_button" onClick={submitEvent}>
              {loading ? <Loader /> : "Create Event"}
            </button>
          ) : (
            // Else, prompt to add subject via disabled state
            <button className="create__event_disabled" disabled>
              Add at least two options
            </button>
          )}
        </div>
      </div>

      {/* Global styling */}
      <style jsx global>{`
        .create__settings_section > input,
        .create__settings_datetime > input {
          width: calc(100% - 10px);
          font-size: 26px !important;
          border-radius: 5px;
          border: 1px solid #e7eaf3;
          margin-top: 15px;
          padding: 5px 0px 5px 5px;
        }

        .accordion__button {
          background-color: #e7eaf3;
          max-width: calc(100% - 36px);
        }

        .accordion__button:hover {
          background-color: #dde1ee;
        }

        .accordion__value {
          margin: 0px 0px 10px 0px;
          width: 100%;
        }

        .accordion__value > label {
          display: block;
          color: #587299;
          font-weight: bold;
          font-size: 18px;
          text-transform: uppercase;
        }

        .accordion__value > textarea {
          width: calc(100% - 10px);
          max-width: calc(100% - 10px);
          font-size: 18px;
          border-radius: 5px;
          border: 1px solid #e7eaf3;
          margin-top: 5px;
          padding: 8px 5px;
          font-family: "Roboto", sans-serif;
        }

        .accordion__value > a {
          text-decoration: none;
          color: #0f0857;
          transition: 50ms ease-in-out;
          font-size: 18px;
          display: inline-block;
          max-width: 100%;
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
        }

        .accordion__value > a:hover {
          opacity: 0.8;
        }

        .accordion__buttons {
          text-align: center;
          padding-top: 10px;
        }

        .accordion__buttons > button {
          padding: 8px 15px;
          display: inline-block;
          border-radius: 5px;
          background-color: #0f0857;
          color: #fff;
          font-size: 16px;
          transition: 100ms ease-in-out;
          border: none;
          cursor: pointer;
          margin: 0px 10px;
        }

        .accordion__buttons > button:nth-child(2) {
          background-color: #ff1c48;
        }

        .accordion__buttons > button:hover {
          opacity: 0.8;
        }

        div:focus,
        button:focus {
          outline: none;
        }
      `}</style>

      {/* Scoped styling */}
      <style jsx>{`
        .create {
          padding-bottom: 80px;
        }

        .create__content {
          max-width: 700px;
          padding: 30px 20px 0px 20px;
          margin: 0px auto;
        }

        .create__content > h1 {
          font-size: 35px;
          color: #0f0857;
          margin: 0px;
        }

        .create__content > p,
        .create__settings > p {
          font-size: 18px;
          line-height: 150%;
          color: rgb(107, 114, 128);
          margin-block-start: 10px;
        }

        .create__settings {
          text-align: left;
          width: calc(100% - 40px);
          max-width: 660px;
          padding: 20px 20px;
          margin: 0px auto;
        }

        .create__settings > h2 {
          color: #0f0857;
          margin-block-end: 0px;
        }

        .create__settings > h3 {
          color: #0f0857;
          transform: translate(5px, 15px);
        }

        .create__settings > p {
          margin-block-start: 5px;
        }

        .create__settings_section {
          background-color: #fff;
          background-color: #fff;
          border-radius: 8px;
          border: 1px solid #e7eaf3;
          box-shadow: 0 0 35px rgba(127, 150, 174, 0.125);
          padding: 15px;
          width: calc(100% - 30px);
          margin: 25px 0px;
        }

        .create__settings_section > label,
        .create__subject_form > div > label {
          display: block;
          color: #587299;
          font-weight: bold;
          font-size: 18px;
          text-transform: uppercase;
        }

        .create__settings_section > p {
          margin: 0px;
        }

        .create__subject_form > div {
          margin: 20px 0px;
        }

        .create__subject_form > div:nth-child(1) {
          margin-top: 0px;
        }

        .create__subject_form > div > input,
        .create__subject_form > div > textarea {
          width: calc(100% - 10px);
          max-width: calc(100% - 10px);
          font-size: 18px;
          border-radius: 5px;
          border: 1px solid #e7eaf3;
          margin-top: 5px;
          padding: 8px 5px;
          font-family: "Roboto", sans-serif;
        }

        .create__subject_form > button,
        .create__event_button,
        .create__event_disabled {
          padding: 12px 0px;
          width: 100%;
          display: inline-block;
          border-radius: 5px;
          background-color: #0f0857;
          color: #fff;
          font-size: 18px;
          transition: 100ms ease-in-out;
          border: none;
          cursor: pointer;
        }

        .button__disabled,
        .create__event_disabled {
          background-color: #e7eaf3 !important;
          color: #000 !important;
          cursor: not-allowed !important;
        }

        .button__disabled:hover,
        .create__event_disabled:hover {
          opacity: 1 !important;
        }

        .create__subject_form > button:hover,
        .create__event_button:hover {
          opacity: 0.8;
        }

        .empty__subjects {
          color: #587299;
          display: block;
          text-align: center;
        }

        .create__submission {
          margin: 0px auto;
          max-width: 660px;
          padding: 50px 20px;
        }
      `}</style>
    </Layout>
  );
}