VandyHacks/vaken

View on GitHub
src/client/routes/dashboard/HackerDash.tsx

Summary

Maintainability
C
1 day
Test Coverage
D
61%
import React, { FunctionComponent, useState, useEffect, RefObject } from 'react';
import styled from 'styled-components';
import { toast } from 'react-toastify';
import { Button } from '../../components/Buttons/Button';
import FloatingPopup from '../../components/Containers/FloatingPopup';
import { FlexColumn, FlexStartColumn } from '../../components/Containers/FlexContainers';
import { Title } from '../../components/Text/Title';
import STRINGS from '../../assets/strings.json';
import { ButtonOutline, CenterButtonText } from '../../components/Buttons/Buttons';
import applicationStatusSVG from '../../assets/img/application_status.svg';
import { SmallCenteredText } from '../../components/Text/SmallCenteredText';
import {
    ApplicationStatus,
    useMyStatusQuery,
    useConfirmMySpotMutation,
    useDeclineMySpotMutation,
} from '../../generated/graphql';

const Confetti = React.lazy(() => import('react-confetti'));

const statusConfig = {
    [ApplicationStatus.Created]: {
        actions: [
            {
                action: () => void (window.location.href = '/application'),
                actionText: 'Complete your application',
            },
        ],
        boldText: "You haven't started your application yet.",
        img: applicationStatusSVG,
        status: 'Not Started',
        statusBG: STRINGS.APPLICATION_INCOMPLETE_STATUSBG,
        statusColor: STRINGS.APPLICATION_INCOMPLETE_STATUSCOLOR,
        text: `The deadline is ${STRINGS.DEADLINE}`,
    },
    [ApplicationStatus.Started]: {
        actions: [
            {
                action: () => void (window.location.href = '/application'),
                actionText: 'Complete your application',
            },
        ],
        boldText: 'You still need to finish your application.',
        img: applicationStatusSVG,
        status: 'Incomplete',
        statusBG: STRINGS.APPLICATION_INCOMPLETE_STATUSBG,
        statusColor: STRINGS.APPLICATION_INCOMPLETE_STATUSCOLOR,
        text: `The deadline is ${STRINGS.DEADLINE}`,
    },
    [ApplicationStatus.Submitted]: {
        actions: [
            {
                action: () => void (window.location.href = '/application'),
                actionText: 'Update your application',
            },
        ],
        boldText: "Thanks for applying! We'll get back to you with a decision shortly.",
        img: applicationStatusSVG,
        status: 'Submitted',
        statusBG: STRINGS.APPLICATION_COMPLETE_STATUSBG,
        statusColor: STRINGS.APPLICATION_COMPLETE_STATUSCOLOR,
        text: 'You may update your responses at any time by re-visiting the application.',
    },
    [ApplicationStatus.Confirmed]: {
        actions: [
            {
                action: () => void (window.location.href = '/api/auth/discord'),
                actionText: 'Join our Discord',
            },
        ],
        boldText: `Woohoo! We'll see you ${STRINGS.START_DAY}! Don't forget to join the Discord!`,
        img: applicationStatusSVG,
        status: 'Confirmed',
        statusBG: STRINGS.APPLICATION_COMPLETE_STATUSBG,
        statusColor: STRINGS.APPLICATION_COMPLETE_STATUSCOLOR,
        text: "If you don't have a team, you can form one when you join the Discord!",
    },
    [ApplicationStatus.Accepted]: {
        actions: [
            {
                action: () => void undefined, // Overridden in HackerDash component
                actionText: 'Confirm your spot',
            },
            {
                action: () => void undefined, // Overridden in HackerDash component
                actionText: 'Decline your spot',
            },
        ],
        boldText: "You've been accepted!",
        img: applicationStatusSVG,
        status: 'Accepted',
        statusBG: STRINGS.APPLICATION_COMPLETE_STATUSBG,
        statusColor: STRINGS.APPLICATION_COMPLETE_STATUSCOLOR,
        text: "Confirm your spot to let us know you'll be coming",
    },
    [ApplicationStatus.Declined]: {
        actions: [],
        boldText: "You've declined.",
        img: applicationStatusSVG,
        status: 'Declined',
        statusBG: STRINGS.APPLICATION_DECLINED_STATUSBG,
        statusColor: STRINGS.APPLICATION_DECLINED_STATUSCOLOR,
        text: `Changed your mind? Email ${STRINGS.HELP_EMAIL}`,
    },
    [ApplicationStatus.Rejected]: {
        actions: [],
        boldText: "Unfortunately, we couldn't offer you a spot this year :(",
        img: applicationStatusSVG,
        status: 'Denied',
        statusBG: STRINGS.APPLICATION_INCOMPLETE_STATUSBG,
        statusColor: STRINGS.APPLICATION_INCOMPLETE_STATUSCOLOR,
        text: '',
    },
};

const HackerDashBG = styled(FloatingPopup)`
    border-radius: 8px;
    height: min-content;
    width: 36rem;
    background: ${STRINGS.BACKGROUND_DARK_SECONDARY};
    padding: 1.5rem;
    text-align: center;

    @media screen and (max-width: 456px) {
        width: 100%;
    }

    @media screen and (max-width: 400px) {
        a {
            width: 100%;
        }
    }

    img {
        display: block;
        height: 200px;
    }

    #actions {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        width: inherit;
        gap: 0.8rem;
    }
`;

export const HackerDash: FunctionComponent = (): JSX.Element => {
    const { data, loading } = useMyStatusQuery();
    const [statusInfo, setStatusInfo] = useState(statusConfig[ApplicationStatus.Created]);
    const [confirmMySpot] = useConfirmMySpotMutation();
    const [declineMySpot] = useDeclineMySpotMutation();
    const [showConfetti, setShowConfetti] = useState(false);

    useEffect((): void => {
        if (data && data.me && data.me.__typename === 'Hacker') {
            console.log(data.me.status);
            setStatusInfo(statusConfig[data.me.status as keyof typeof statusConfig]);
        }
    }, [data]);

    useEffect((): void => {
        statusConfig[ApplicationStatus.Accepted].actions[0].action = () => {
            // eslint-disable-next-line no-alert
            const confirmed = window.confirm('Are you sure you want to confirm your spot?');
            if (confirmed) {
                confirmMySpot()
                    .then(() => setShowConfetti(true))
                    .catch(err => console.error(err));
            }
            return undefined;
        };
        statusConfig[ApplicationStatus.Accepted].actions[1].action = () => {
            // eslint-disable-next-line no-alert
            const confirmed = window.confirm('Are you sure you want to decline your spot?');
            if (confirmed) {
                declineMySpot()
                    // no confetti :(
                    .catch(err => console.error(err));
            }
            return undefined;
        };
    }, [confirmMySpot, declineMySpot]);

    useEffect((): void => {
        const msg = new URLSearchParams(window.location.search).get('msg');
        if (msg) {
            toast.success(msg, {
                position: 'bottom-right',
            });
        }
    }, []);

    return (
        <>
            <FlexStartColumn>
                {showConfetti ? (
                    <Confetti
                        recycle={false}
                        numberOfPieces={800}
                        canvasRef={(null as unknown) as RefObject<HTMLCanvasElement>}
                    />
                ) : null}
                <HackerDashBG>
                    <FlexColumn>
                        <Title fontSize="1.75rem" color={STRINGS.DARK_TEXT_COLOR}>
                            {STRINGS.HACKER_DASHBOARD_HEADER_TEXT}
                        </Title>
                        {loading ? null : (
                            <>
                                <ButtonOutline
                                    paddingLeft="4rem"
                                    paddingRight="4rem"
                                    width="auto"
                                    background={statusInfo.statusBG}
                                    glowColor="null">
                                    <CenterButtonText color={statusInfo.statusColor} fontSize="1.8rem">
                                        {statusInfo.status}
                                    </CenterButtonText>
                                </ButtonOutline>
                                <img src={statusInfo.img} alt={`${STRINGS.APPLICATION_STATUS_SVG_ALT_TEXT}`} />
                                <SmallCenteredText
                                    color={`${STRINGS.DARK_TEXT_COLOR}`}
                                    fontSize="1rem"
                                    margin="1.4rem">
                                    <>
                                        <p style={{ fontWeight: 'bold' }}>{statusInfo.boldText}</p>
                                        <br />
                                        {statusInfo.text}
                                    </>
                                </SmallCenteredText>
                            </>
                        )}
                        <div id="actions">
                            {statusInfo.actions.map(e => (
                                <Button key={e.actionText} large long onClick={e.action}>
                                    {e.actionText}
                                </Button>
                            ))}
                        </div>
                    </FlexColumn>
                </HackerDashBG>
            </FlexStartColumn>
        </>
    );
};

export default HackerDash;