Skip to content

Commit

Permalink
Dashboard Teams and Points (#77)
Browse files Browse the repository at this point in the history
* Create create team flow

* Create css variable for page fade

* Partially complete leave team and send invite

* Add "Something went wrong" page on error

* Add points feature

* Refresh page after points redemption

* Add functionality to cancel buttons

* Use names from dashboard

* Show invited members

* Small tweaks

* Announcements

* Show invites

* Join team
  • Loading branch information
Wal Wal committed Jan 29, 2021
1 parent 587c683 commit 15f9d30
Show file tree
Hide file tree
Showing 14 changed files with 1,042 additions and 92 deletions.
3 changes: 3 additions & 0 deletions src/components/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const StyledInput = styled.input`

type InputProps = {
placeHolder?: string;
className?: string;
label?: string;
value?: string;
name?: string;
Expand Down Expand Up @@ -77,9 +78,11 @@ const InputComponent = ({
style,
required,
grow,
className,
...rest
}: InputProps) => (
<InputContainer
className={className}
style={{
width: `${expand ? '100%' : 'unset'}`,
padding: `${padded ? '1rem 8px' : 0}`,
Expand Down
1 change: 1 addition & 0 deletions src/components/countdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const StyledCountdown = styled.div`
align-items: flex-start;
padding: 16px;
margin-bottom: 20px;
width: 100%;
Expand Down
4 changes: 3 additions & 1 deletion src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ export * from './faq';
export * from './sponsors';
export {default as Input} from './Input';
export {default as LoadingSymbol} from './loadingSymbol';
export {default as Sidebar} from './sidebar';
export {default as Sidebar} from './sidebar';
export {default as TeamManager} from './teamManager';
export {default as PointsManager} from './pointsManager';
201 changes: 201 additions & 0 deletions src/components/pointsManager.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import React, {useState} from 'react';
import {Link} from 'react-router-dom';
import styled, {css} from 'styled-components';
import {HundredIcon} from '../assets/img/icons';
import {Button, Input, LoadingSymbol} from '../components';
import {useDashboardInfo} from '../hooks';

const Container = styled.div`
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
& > * {
width: 100%;
}
`;

const Card = styled.div`
display: flex;
flex-direction: column;
align-items: center;
border-radius: 8px;
padding: 20px;
background-color: var(--white);
box-shadow: var(--card-shadow);
`;

const normalizeButton = css`
background: none;
border: none;
padding: 0;
font-family: var(--secondary-font);
font-size: 1rem;
&:hover {
cursor: pointer;
}
`;

const glowingButton = css`
display: flex;
align-items: center;
border-radius: 8px;
padding: 20px;
width: 100%;
background: var(--action-gradient);
color: var(--white);
text-align: left;
transition: 100ms ease-out;
box-shadow: var(--card-shadow);
&:hover {
box-shadow: var(--card-shadow-hover);
}
svg {
margin-right: 0.5em;
}
`;

const ActionButton = styled.button`
${normalizeButton}
${glowingButton}
`;

const StyledInput = styled(Input)`
margin: 10px 0 25px;
label {
color: var(--wine);
}
input {
color: var(--indoor);
border-bottom-color: var(--indoor);
&:focus {
border-bottom-color: var(--wine);
}
}
`;

const TextButton = styled.button`
${normalizeButton}
margin-top: 15px;
color: var(--wineLight);
&:hover {
cursor: pointer;
text-decoration: underline;
}
`;

const TeamManager = () => {
const {redeemPoints} = useDashboardInfo();
const [isLoading, setLoading] = useState(false);
const [view, setView] = useState<'default' | 'enterCode' | 'codeEntered'>(
'default'
);
const [code, setCode] = useState('');
const [inputError, setInputError] = useState<string>();
const [pointsRedeemed, setPoints] = useState<number>();

const handleSubmitCode = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
setLoading(true);
setInputError(undefined);

if (code == '') {
setLoading(false);
return setInputError('Please enter a code.');
}

const result = await redeemPoints(code);

switch (result.status) {
case 'success':
setView('codeEntered');
setPoints(result.value);
break;
case 'invalid':
setInputError('Invalid code.');
break;
case 'redeemed':
setInputError(`Code has been redeemed.`);
break;
case 'error':
setInputError('Something went wrong.');
}
setLoading(false);
};

if (isLoading) {
return (
<Container>
<LoadingSymbol color='var(--wine)' />
</Container>
);
}

switch (view) {
case 'enterCode':
return (
<Container>
<Card>
<form onSubmit={handleSubmitCode}>
<StyledInput
onChange={(event: any) => setCode(event.target.value)}
value={code}
label='Points Code'
name='code'
error={inputError}
displayLabel
forceLabel
required
/>

<Button kind='button' color='var(--wineLight)'>
Redeem
</Button>
</form>
<TextButton onClick={() => setView('default')}>Cancel</TextButton>
</Card>
</Container>
);
case 'codeEntered':
return (
<Container>
<Card>
<p>You received {redeemPoints} points!</p>
<Button
kind='button'
color='var(--wineLight)'
action={() => {
setView('default');
setPoints(undefined);
}}
>
Awesome!
</Button>
</Card>
</Container>
);
default:
return (
<Container>
<ActionButton onClick={() => setView('enterCode')}>
<HundredIcon />
Redeem a points code
</ActionButton>
</Container>
);
}
};

export default TeamManager;
20 changes: 7 additions & 13 deletions src/components/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ interface ButtonProps {
const Button = ({icon: Icon, children, link}: ButtonProps) => (
<StyledLink
to={link}
selected={useRouteMatch({path: link, exact: true}) !== null}
selected={
useRouteMatch({path: link, exact: link == '/dashboard/'}) !== null
}
>
<Icon />
{children}
Expand Down Expand Up @@ -170,14 +172,6 @@ const Sidebar = (props: any) => {
Leaderboard
</Button>
</Section>
{user && user.role > 0 && (
<Section>
<SectionHeader>SPONSOR PAGES</SectionHeader>
<Button icon={GavelIcon} link={appendBase('/sponsors/judging')}>
Judging
</Button>
</Section>
)}
{user && user.role > 1 && (
<Section>
<SectionHeader>ORGANIZER PAGES</SectionHeader>
Expand All @@ -187,12 +181,12 @@ const Sidebar = (props: any) => {
>
Send Notifications
</Button>
<Button icon={PencilIcon} link={appendBase('/admin/edit-schedule')}>
{/* <Button icon={PencilIcon} link={appendBase('/admin/edit-schedule')}>
Edit Schedule
</Button>
<Button icon={GraphIcon} link={appendBase('/admin/stats')}>
</Button> */}
{/* <Button icon={GraphIcon} link={appendBase('/admin/stats')}>
Statistics
</Button>
</Button> */}
<Button
icon={HundredIcon}
link={appendBase('/admin/generate-points')}
Expand Down
Loading

0 comments on commit 15f9d30

Please sign in to comment.