rrebase/knboard

View on GitHub
frontend/src/features/profile/Profile.tsx

Summary

Maintainability
B
5 hrs
Test Coverage
/* eslint-disable react/jsx-no-undef */
import React from "react";
import {
  Container,
  Divider,
  TextField,
  Button,
  useTheme,
  WithTheme,
} from "@material-ui/core";
import styled from "@emotion/styled";
import { useDispatch, useSelector } from "react-redux";
import { fetchUserDetail, fetchAvatarList, updateUser } from "./ProfileSlice";
import { RootState } from "store";
import { css } from "@emotion/core";
import { useForm } from "react-hook-form";
import UserAvatar from "./UserAvatar";
import { Alert, AlertTitle } from "@material-ui/lab";
import SEO from "components/SEO";

const Title = styled.h3`
  margin-top: 2rem;
  margin-bottom: 0.5rem;
`;

const FormContainer = styled.div<WithTheme>`
  margin-top: 2rem;
  display: flex;
  align-items: center;
  ${(props) => props.theme.breakpoints.down("xs")} {
    flex-direction: column;
  }
`;

const Row = styled.div``;

const UserForm = styled.form`
  width: 100%;
`;

const Fields = styled.div``;

interface FormData {
  username: string;
  first_name: string;
  last_name: string;
  email: string;
}

const Profile = () => {
  const theme = useTheme();
  const dispatch = useDispatch();
  const userDetail = useSelector(
    (state: RootState) => state.profile.userDetail
  );
  const apiErrors = useSelector((state: RootState) => state.profile.apiErrors);
  const loading = useSelector((state: RootState) => state.profile.loading);
  const { register, errors, handleSubmit, setError } = useForm<FormData>({
    mode: "onChange",
  });

  React.useEffect(() => {
    dispatch(fetchUserDetail());
    dispatch(fetchAvatarList());
  }, [userDetail?.id]);

  React.useEffect(() => {
    if (apiErrors) {
      for (const errorKey in apiErrors) {
        // @ts-ignore
        setError(errorKey, "api_error", apiErrors[errorKey]);
      }
    }
  }, [apiErrors]);

  if (!userDetail) {
    return null;
  }

  const onSubmit = async (data: FormData) => {
    dispatch(updateUser({ ...userDetail, ...data }));
  };

  return (
    <Container maxWidth="sm">
      <SEO title="Profile" />
      {userDetail.is_guest && (
        <Alert
          severity="warning"
          variant="outlined"
          css={css`
            margin: 1rem 0;
          `}
        >
          <AlertTitle>Warning</AlertTitle>
          Guest accounts are deleted 24 hours after creation!
        </Alert>
      )}
      <Title>About</Title>
      <Divider />
      <FormContainer theme={theme}>
        <UserAvatar />
        <UserForm onSubmit={handleSubmit(onSubmit)}>
          <Fields>
            <Row>
              <TextField
                id="username"
                name="username"
                inputRef={register({ required: "This field is required" })}
                defaultValue={userDetail.username}
                helperText={errors.username?.message}
                error={Boolean(errors.username)}
                label="Username"
                variant="outlined"
                margin="dense"
                fullWidth
              />
            </Row>
            <Row>
              <TextField
                id="first_name"
                name="first_name"
                inputRef={register()}
                defaultValue={userDetail.first_name}
                helperText={errors.first_name?.message}
                error={Boolean(errors.first_name)}
                label="First name"
                variant="outlined"
                margin="dense"
                fullWidth
              />
            </Row>
            <Row>
              <TextField
                id="last_name"
                name="last_name"
                inputRef={register()}
                defaultValue={userDetail.last_name}
                helperText={errors.last_name?.message}
                error={Boolean(errors.last_name)}
                label="Last name"
                variant="outlined"
                margin="dense"
                fullWidth
              />
            </Row>
            <Row>
              <TextField
                id="email"
                name="email"
                inputRef={register({
                  pattern: {
                    value: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
                    message: "Invalid email",
                  },
                })}
                defaultValue={userDetail.email}
                helperText={errors.email?.message}
                error={Boolean(errors.email)}
                label="Email"
                variant="outlined"
                margin="dense"
                fullWidth
              />
            </Row>
            <Button
              variant="contained"
              color="primary"
              type="submit"
              disabled={loading}
              data-testid="profile-save"
              css={css`
                margin: 1rem 0;
                text-align: right;
              `}
            >
              Save
            </Button>
          </Fields>
        </UserForm>
      </FormContainer>
    </Container>
  );
};

export default Profile;