Друзья, всем привет! Меня зовут Игорь Карелин, я frontend-разработчик в компании Домклик. Как вы помните из предыдущих частей (1, 2, 3), у нас уже есть функциональность для работы с планировщиком в бэкенде. Теперь нашей задачей будет создать удобный и интуитивно понятный пользовательский интерфейс, чтобы пользователи могли легко взаимодействовать с задачами.

Для начала мы определим структуру нашего интерфейса. Нам понадобятся следующие элементы:

  1. Форма регистрации позволит новым пользователям создать аккаунт. Она будет содержать поля для ввода имени, электронной почты и пароля, а также кнопку «Зарегистрироваться».

  2. Форма входа для зарегистрированных пользователей. Она будет содержать поля для ввода электронной почты и пароля, а также кнопку «Войти».

  3. Форма создания новой задачи будет содержать поля для ввода названия и описания задачи, а также кнопку «Создать».

  4. Список задач, доступных для пользователя. Каждая задача будет содержать название, описание, статус и кнопку удаления.

  5. Кнопка «Logout» (Выход) будет располагаться в верхнем правом углу интерфейса, чтобы пользователи могли легко завершить свою сессию и выйти из аккаунта. При нажатии на неё пользователь будет перенаправляться на страницу входа. Это важный элемент для обеспечения безопасности и управления аккаунтом.

При разработке нашего пользовательского интерфейса будем использовать следующие библиотеки и инструменты:

  1. React — это JavaScript-библиотека для создания пользовательских интерфейсов. Она позволяет легко создавать компоненты и управлять состоянием приложения.

  2. React Hook Form — это библиотека для управления формами в React‑приложениях с использованием хуков. Она предоставляет удобные средства для валидации и обработки данных из форм.

  3. Zustand — это библиотека для управления глобальным состоянием в React‑приложениях. Она позволяет легко создавать и обновлять глобальное состояние без необходимости использовать Redux или другие более сложные решения.

  4. Axios — это библиотека для выполнения HTTP-запросов в браузере и на сервере. Она будет полезна для взаимодействия с бэкендом нашего приложения, например, при отправке и получении данных о задачах.

  5. Material‑UI — это библиотека компонентов для React, которая предоставляет набор готовых стилизованных компонентов, включая кнопки, текстовые поля, таблицы и другие элементы, соответствующие дизайну Material Design от Google. Она упрощает создание красочных и современных интерфейсов.

С помощью этих библиотек и инструментов мы сможем эффективно разработать пользовательский интерфейс нашего приложения, удобно, гибко и с хорошим дизайном.

Создание формы регистрации: шаг к созданию безопасных и удобных аккаунтов

Форма регистрации — это первый шаг, который пользователь выполняет при взаимодействии с вашим веб-сервисом или приложением. Она должна быть интуитивно понятной, привлекательной и безопасной. Это не только облегчает регистрацию, но и создаёт положительное впечатление о вашем продукте. Давайте подробно рассмотрим, какие ключевые элементы должны присутствовать в форме регистрации:

  1. Поле для имени. Пользователи должны иметь возможность ввести свое имя. Это может быть именем и фамилией, или просто псевдонимом.

  2. Поле для адреса электронной почты. Электронная почта является уникальным идентификатором пользователя. Необходимо убедиться, что форма проверяет введенный адрес на корректность.

  3. Поле для пароля. Создание надёжного пароля — важный аспект безопасности. Мы упростили проверку пароля и ограничились максимальной длиной символов.

  4. Кнопка «Зарегистрироваться». Элементарно, но не менее важно. Кнопка должна быть яркой и легкозаметной, чтобы пользователям было проще завершить регистрацию.

Реализуем форму и обработчик её отправки с помощью React Hook Form и Material-UI:

export const RegisterForm = () => {
  const { fetchLoginUser } = useUserState();
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<IRegisterForm>();
  const onSubmit: SubmitHandler<IRegisterForm> = (loginFormData) =>
    fetchLoginUser(loginFormData);

  return (
    <StyledForm onSubmit={handleSubmit(onSubmit)}>
      <StyledTextField
        label="Username"
        type={'text'}
        autoComplete="new-username"
        {...register('username', { required: true })}
      />
      <StyledTextField
        label="Email"
        type={'email'}
        autoComplete="new-email"
        {...register('email', { required: true })}
      />
      <StyledTextField
        label="Password"
        type={'password'}
        autoComplete="new-password"
        {...register('password', {
          required: true,
          minLength: {
            value: 6,
            message: 'Password must have at least 6 characters',
          },
        })}
      />
      {errors.password?.message && (
        <Alert style={{ marginTop: 16 }} severity="error">
          {errors.password.message}
        </Alert>
      )}
      <StyledButton variant="contained" color="primary" type="submit">
        Зарегестрироваться
      </StyledButton>
    </StyledForm>
  );
};

Компонент RegisterForm представляет собой форму регистрации пользователя в приложении. Давайте разбёрем его основные аспекты:

  1. fetchLoginUser извлекается из хука useUserState(). Эта функция, используется для отправки запроса на сервер и выполнения входа пользователя.

  2. Для управления формой используется библиотека React Hook Form (useForm). Она предоставляет удобные инструменты для работы с формами в React.

  3. Компонент имеет три поля ввода: "Username" (имя пользователя), "Email" (адрес электронной почты) и "Password" (пароль). Каждое поле связано с соответствующим полем данных формы через register. Например, {...register('username', { required: true })} связывает поле "Username" с данными формы и указывает, что оно обязательное для заполнения.

  4. Поле "Password" имеет дополнительную валидацию. Оно должно быть не менее 6 символов длиной, и в случае невыполнения этого условия, будет отображено сообщение об ошибке.

  5. В случае наличия ошибки в поле "Password" отображается компонент Alert с сообщением об ошибке.

  6. Кнопка «Зарегистрироваться» позволяет отправить данные формы на сервер при нажатии. Это делается через обработчик onSubmit.

Создание эффективной формы регистрации — это важный шаг на пути к созданию успешного онлайн‑продукта. Уделяйте внимание деталям, обеспечивайте безопасность и интуитивность, и ваши пользователи будут довольны процессом регистрации.

Создание формы входа: простой и безопасный способ получить доступ к вашему аккаунту

Прежде чем начать разработку, давайте определим, какие элементы должны присутствовать в форме входа:

  1. Поле для адреса электронной почты. Предназначено для ввода адреса электронной почты, который был указан при регистрации.

  2. Поле для пароля. Зарегистрированный пользователь должен ввести свой пароль для подтверждения личности.

  3. Кнопка «Войти». Основной элемент, который инициирует процесс входа.

export const AuthForm = () => {
  const { fetchLoginUser } = useUserState();
  const { register, handleSubmit } = useForm<IAuthForm>();
  const onSubmit: SubmitHandler<IAuthForm> = (loginFormData) =>
    fetchLoginUser(loginFormData);

  return (
    <StyledForm onSubmit={handleSubmit(onSubmit)}>
      <StyledTextField
        label="Email"
        type={'email'}
        autoComplete="current-email"
        {...register('email', { required: true })}
      />
      <StyledTextField
        label="Password"
        autoComplete="current-password"
        type={'password'}
        {...register('password', { required: true })}
      />
      <StyledButton variant="contained" color="primary" type="submit">
        Войти
      </StyledButton>
    </StyledForm>
  );
};

Компонент AuthForm представляет собой интерфейс для аутентификации пользователей в приложении. Эта форма имеет сходную структуру с формой регистрации. Отдельное разделение форм регистрации и входа облегчает будущее внесение изменений и рефакторинга. При отправке формы данные будут переданы в функцию fetchLoginUser для выполнения необходимых действий для входа пользователя в приложение.

Форма создания новой задачи: эффективный способ управления заданиями

Создание новых задач — это ключевая часть любого приложения для управления заданиями. Форма позволяет пользователям быстро и легко добавлять новые задачи в систему. Давайте подробно рассмотрим, как её разработать. Чтобы форма была полезной и удобной, она должна включать в себя:

  1. Поле для названия задачи. Здесь пользователь указывает краткое название задачи.

  2. Поле для описания задачи. Позволяет подробнее описать суть задачи, указать сроки выполнения или другую информацию.

  3. Кнопка «Создать». Запускает процесс создания новой задачи на основе введённых данных.

export const TaskManagementForm = () => {
  const { register, reset, handleSubmit } = useForm<TaskForm>();
  const { fetchCreateTask } = useTasksStore();

  const onSubmit: SubmitHandler<TaskForm> = (data) => {
    fetchCreateTask(data);
    reset();
  };

  return (
    <StyledForm onSubmit={handleSubmit(onSubmit)}>
      <StyledTextField
        label={'Заголовок'}
        type={'text'}
        {...register('title', { required: true })}
      />
      <StyledTextField
        label={'Описание'}
        type={'text'}
        multiline
        rows={4}
        {...register('description', { required: true })}
      />
      <Button type={'submit'} color={'primary'} variant={'contained'}>
        Создать
      </Button>
    </StyledForm>
  );

Компонент TaskManagementForm представляет собой форму для управления задачами. В данном случае форма предназначена для создания новых задач в системе. Давайте рассмотрим ключевые аспекты этого компонента:

  1. useForm<TaskForm>() используется для управления состоянием формы и её полями. Он предоставляет функции для регистрации полей, сброса значений и обработки отправки формы.

  2. fetchCreateTask(data) — эта функция отправляет данные о новой задаче на сервер и при получении записывает их в хранилище.

  3. onSubmit — это функция-обработчик, вызываемая при отправке формы. Она получает данные формы (название и описание задачи) и передаёт их функции fetchCreateTask для создания задачи. После отправки данных форма сбрасывает значения полей с помощью reset().

  4. Форма включает поля «Заголовок» и «Описание». Каждое из них связано с соответствующими данными формы через register. Также указывается, что оба поля обязательны для заполнения, что гарантирует их проверку перед отправкой данных.

  5. Кнопка «Создать» позволяет пользователю запустить процесс создания новой задачи с помощью отправки данных формы.

Этот компонент подразумевает обязательную проверку полей перед отправкой данных. После успешной отправки форма сбрасывает значения полей для создания следующей задачи.

Список задач: организация и управление вашими заданиями

Список задач — это один из ключевых компонентов любого приложения для управления заданиями. Мы рассмотрим, как создать элемент списка задач, который будет содержать информацию о названии, описании, статусе и опции удаления для каждой задачи.

export const TaskRow = ({
  completed,
  _id,
  title,
  description,
  handleToggle,
  onClickDeleteIcon,
}: Props) => {
  return (
    <ListItem
      color={'primary'}
      secondaryAction={
        <IconButton
          edge="end"
          aria-label="delete"
          onClick={() => onClickDeleteIcon(_id)}
        >
          <DeleteIcon />
        </IconButton>
      }
      disablePadding
    >
      <ListItemButton
        role={undefined}
        onClick={() =>
          handleToggle({ _id, title, description, completed: !completed })
        }
        dense
      >
        <ListItemIcon>
          <Checkbox
            edge="start"
            checked={completed}
            tabIndex={-1}
            disableRipple
            inputProps={{ 'aria-labelledby': _id }}
          />
        </ListItemIcon>
        <ListItemText id={_id} primary={title} secondary={description} />
      </ListItemButton>
    </ListItem>
  );
};

Компонент TaskRow представляет собой строку (или элемент) в списке задач. Он отображает информацию о задаче: название (title), описание (description) и статус выполнения (completed), а также предоставляет опцию для переключения статуса и удаления задачи. Давайте рассмотрим ключевые аспекты этого компонента:

  1. completed — это свойство указывает, выполнена ли задача. В зависимости от него будет установлено состояние флажка Checkbox и цвет строки задачи.

  2. _id — уникальный идентификатор задачи, который может использоваться для её идентификации в списке.

  3. title — название задачи, которое будет отображено в строке задачи.

  4. description — описание задачи, которое может содержать дополнительную информацию.

  5. handleToggle — функция, которая вызывается при переключении статуса задачи (выполнено/не выполнено). Она принимает объект с данными о задаче и новым состоянием completed.

  6. onClickDeleteIcon — функция вызывается при нажатии на иконку удаления задачи. Она принимает _id задачи, которую нужно удалить.

  7. Компонент ListItem из Material‑UI используется для создания строки задачи. Он также включает в себя кнопку удаления задачи (IconButton с иконкой DeleteIcon) в правой части строки.

  8. Внутри ListItem расположен компонент ListItemButton, который реагирует на клик пользователя. При клике на строку задачи, вызывается функция handleToggle для переключения статуса выполнения задачи.

  9. Компонент ListItemIcon содержит флажок Checkbox, который отображает текущий статус выполнения задачи. В зависимости от значения completed, флажок будет установлен в «выполнено» или «не выполнено».

  10. Компонент ListItemText отображает название и описание задачи внутри строки.

Этот компонент предоставляет интерактивный элемент списка задач, который позволяет пользователям переключать статус выполнения задач и удалять их из списка.

Выход: обеспечение безопасности и управление аккаунтом

«Выход» является неотъемлемой частью интерфейса любого приложения с аутентификацией пользователей. Эта кнопка позволяет пользователям безопасно завершать свою сессию и выходить из своего аккаунта. Давайте рассмотрим её ключевые аспекты:

  1. Безопасность. После выхода из аккаунта никакие действия, совершаемые от имени пользователя, больше не могут быть выполнены без повторной аутентификации.

  2. Управление аккаунтом. Кнопка предоставляет пользователям удобный способ управлять своей сессией и аккаунтом, если пользователь хочет выйти из одного аккаунта и войти под другим.

  3. Защита данных. После выхода из аккаунта доступ к личным данным и функциональности пользователя должен быть ограничен. Это помогает предотвратить несанкционированный доступ к аккаунту.

export const Header = () => {
  const { resetUser } = useUserState();
  const { accessToken } = useAccessToken();

  return (
    <Box sx={{ flexGrow: 1 }}>
      <AppBar position="static">
        <Toolbar>
          <Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
            ToDoApp
          </Typography>
          {accessToken && (
            <Button color="inherit" onClick={resetUser}>
              Logout
            </Button>
          )}
        </Toolbar>
      </AppBar>
    </Box>
  );
};

Компонент Header представляет собой верхнюю панель (шапку) интерфейса приложения. В нём рассматривается реализация кнопки «Выход», которая позволяет пользователям завершать свою сессию и выходить из аккаунта. Давайте подробно разберём этот компонент:

  1. resetUser — функция, полученная из хука useUserState(), используется для завершения сессии пользователя и выхода из аккаунта. Она вызывается при нажатии на кнопку «Выход» для выполнения соответствующих действий.

  2. accessToken — значение, полученное из хука useAccessToken(). Служит индикатором аутентификации пользователя. Если accessToken существует (то есть пользователь аутентифицирован), то кнопка «Выход» будет отображаться.

  3. Button — компонент кнопки, который отображает кнопку «Выход». color="inherit" задает цвет текста кнопки в соответствии с текущей темой (обычно белый или черный текст). onClick={resetUser} указывает, что при нажатии на кнопку будет вызвана функция resetUser, что приведёт к завершению сессии пользователя и выходу из аккаунта.

Компонент Header обеспечивает навигацию в приложении и позволяет пользователям безопасно выходить из аккаунта, если они аутентифицированы.

Заключение

В этой, четвёртой части нашей серии статей «Пишем продвинутый планировщик с использованием React, Nest и NX», мы углубились в мир пользовательского интерфейса. Мы рассмотрели создание формы регистрации и входа, а также разработали функциональность создания и отображения задач. Мы использовали множество инструментов, таких как React, React Hook Form, Zustand, Axios и Material UI, чтобы создать более функциональный и интерактивный пользовательский интерфейс.

Я представил лишь часть кода, который был написан в процессе разработки приложения, чтобы сделать статью доступнее и понятнее для широкой аудитории читателей, независимо от их уровня опыта в программировании. Я сосредоточился на ключевых моментах и основных концепциях, чтобы облегчить понимание общей структуры приложения.

Однако, как и в любом проекте, существует множество аспектов, которые могли бы быть описаны подробнее. У каждого из нас может быть собственный подход к решению задачи, и существует несколько путей для достижения того же результата. Также существуют лучшие практики и паттерны, которые можно было бы применить для улучшения кода.

Я предлагаю рассматривать серию статей как отправную точку или введение в тему. Разработка приложений — это постоянный процесс самообразования и совершенствования, и важно помнить, что всегда есть место для улучшений и оптимизаций.

Дорогие читатели! Я выражаю вам глубокую благодарность за то, что вы уделили время и внимание для серии статей «Пишем продвинутый планировщик с использованием React, Nest и NX». Ваш интерес и поддержка значат очень многое. Я надеюсь, что статья была для вас полезной и информативной.

Исходный код доступен по ссылке.

Комментарии (1)


  1. Cere8ellum
    22.09.2023 17:43
    +1

    Спасибо за материал. Как раз пишу сейчас подобное. Застрял на унифицировании компонента формы. Идея в том, чтобы тело формы было одно и тоже, а вот наполнение полями разное (логин, регистрация, и куча других форм). Этакий HOC. Register передаю в цикле (React.children.map) для каждого чайлда (инпута). Работает. Но ошибки не выводятся. Есть свет в конце тоннеля :)