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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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">
<
<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>
</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 |
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 |
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 |
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 |
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 |
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 |
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>
);
}