0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Reactの勉強環境を準備する③

Posted at

今回の狙い
フロント処理となるREACT(MUI)を利用して、入力画面機能を準備します。
前回の記事の環境を、そのまま使って、Reactのプログラムだけ差し替えます。

Reactで作成する入力画面のイメージ
image.png

狙いとしては、チェック処理やメッセージ等は、後からユーザ要望が出てくるので、簡単に修正できるように、別ファイルにしたいと思います。

RedmineのRestAPI
RedmineのAPIを利用して、Redmineのユーザを登録したいと思います。

APIの仕様としては、POSTアクセスで、ヘッダー情報にAPIキーをセットして、登録するユーザ情報をJSONを渡す事になります。

Reactのプログラム
以下のプログラムを用意します。

フォルダ構成
└── src
    ├── index.js   (初期起動プログラム)
    ├── App.js    (画面へ表示される本体)
    ├── ErrCheck.js (エラーチェック処理部)
    └── msg.jp.js  (エラーメッセージ部)
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
App.js
import { useForm } from 'react-hook-form';
import React from 'react';
import { TextField, Button } from '@mui/material';
import Grid from '@mui/material/Grid';
import ErrCheck from './ErrCheck';
import { yupResolver } from '@hookform/resolvers/yup';

export default function App() {
  const { register, handleSubmit, formState: { errors } } = useForm({
    defaultValues: {
      loginid: '1234567890',
      passwd: '12345Aa!',
      firstnm: 'a',
      lastnm: 'b',
      mailad: 'c@d'
    },
    resolver: yupResolver(ErrCheck),
  });

  const onsubmit = async (data) => {
    try {
      const response = await fetch('http://localhost:3001/users.json', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-Redmine-API-Key': '※ここにredmineのAPIキー',
        },
        body: JSON.stringify({
          user: {
            login: data.loginid,
            password: data.passwd,
            firstname: data.firstnm,
            lastname: data.lastnm,
            mail: data.mailad,
          },
        }),
      });

      if (response.ok) {
        const responseData = await response.json();
        console.log('Redmine登録成功:', responseData);
        alert('ユーザー登録が完了しました。');
      } else {
        const errorData = await response.json();
        console.error('Redmine登録失敗:', errorData);
        alert('ユーザー登録に失敗しました。');
      }
    } catch (error) {
      console.error('送信エラー:', error);
      alert('送信中にエラーが発生しました。');
    }
  };

  const onerror = err => console.log(err);
  return (
    <div className="App">
    <form onSubmit={handleSubmit(onsubmit, onerror)} noValidate>
    <Grid container spacing={1}>
      <Grid size={{xs:12}}>
        <TextField id="loginid" type="text" label={ErrCheck.fields.loginid.describe().label}
                    error={!!errors.loginid}
                    helperText={errors.loginid?.message}
            {...register('loginid')} />
      </Grid>
      <Grid size={{xs:12}}>
        <TextField id="passwd" type="password" label={ErrCheck.fields.passwd.describe().label}
                    error={!!errors.passwd}
                    helperText={errors.passwd?.message}
            {...register('passwd')} />
      </Grid>
      <Grid size={{xs:12}}>
        <TextField id="firstnm" type="text" label={ErrCheck.fields.firstnm.describe().label}
                    error={!!errors.firstnm}
                    helperText={errors.firstnm?.message}
            {...register('firstnm')} />
      </Grid>
      <Grid size={{xs:12}}>
        <TextField id="lastnm" type="text" label={ErrCheck.fields.lastnm.describe().label}
                    error={!!errors.lastnm}
                    helperText={errors.lastnm?.message}
            {...register('lastnm')} />
      </Grid>
      <Grid size={{xs:12}}>
        <TextField id="mailad" type="text" label={ErrCheck.fields.mailad.describe().label}
                    error={!!errors.mailad}
                    helperText={errors.mailad?.message}
            {...register('mailad')} />
      </Grid>
      <Grid size={{xs:12}}>
        <Button variant="contained" type="submit">登録</Button>
      </Grid>
    </Grid>
    </form>
    </div>
  );
}
ErrCheck.js
import msg from './msg.jp';

const hasMixedCase = /(?=.*[a-z])(?=.*[A-Z])/;
const hasNumber = /(?=.*[0-9])/;
const hasSymbol = /(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?])/;

const ErrCheck = msg.object({
  loginid  : msg.string().label('ログインID')
         .required()
         .max(10),
  passwd  : msg.string().label('パスワード')
         .min(8)
         .required()
         .matches(hasMixedCase, 'パスワードは英大文字と英小文字を含めてください。')
         .matches(hasNumber, 'パスワードは数字を含めてください。')
         .matches(hasSymbol, 'パスワードは記号を含めてください。'),
  firstnm  : msg.string().label('')
         .required(),
  lastnm  : msg.string().label('')
         .required(),
  mailad  : msg.string().label('メールアドレス')
         .required()
         .email(),
});
export default ErrCheck;
msg.jp.js
import * as yup from 'yup';

const jpLocale = {
  mixed: {
    required: param => `${param.label}は必須です。`,
  },
  string: {
    min: param => `${param.label}${param.min}文字以上でなければなりません。`,
    max: param => `${param.label}${param.max}文字以下でなければなりません。`,
    matches: param => `${param.label}は「${param.regex}」形式に一致していなければなりません。`,
    email: param => `${param.label}はメールアドレス形式でなければなりません。`,
  },
};
yup.setLocale(jpLocale);
export default yup;

入力エラーの場合
赤文字+赤枠になり、下段にエラーメッセージが表示されます。
image.png
ちなみに、RedmineのAPIにも、エラーチェックが実装されています。
 1)同じ「ログインID」は登録できない
 2)同じ「メールアドレス」は登録できない
エラーの場合は、以下のポップアップ画面が表示されるはずです。
image.png
正常に登録されると、以下のポップアップ画面が表示されるはずです。
image.png
登録したユーザで、RedmineにログインできればOKです。

お疲れ様でしたぁ~。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?