## 概要 [Task2315: ユーザー編集ポップアップ実装](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2315) - ユーザー編集ポップアップを実装しました。 ## レビューポイント - デザイン判定は適切か - 表示文言は適切か - エラーチェックは適切か ## UIの変更 - [Task2315](https://ndstokyo.sharepoint.com/:f:/r/sites/Piranha/Shared%20Documents/General/OMDS/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88/Task2315?csf=1&web=1&e=AP7b9M) ## 動作確認状況 - ローカルで確認
230 lines
6.7 KiB
TypeScript
230 lines
6.7 KiB
TypeScript
import { RootState } from "app/store";
|
||
import { USER_ROLES } from "components/auth/constants";
|
||
import {
|
||
AddUser,
|
||
RoleType,
|
||
UserView,
|
||
isLicenseStatusType,
|
||
isRoleType,
|
||
} from "./types";
|
||
import { LICENSE_STATUS } from "./constants";
|
||
|
||
export const selectInputValidationErrors = (state: RootState) => {
|
||
const { name, email, role, authorId, encryption, encryptionPassword } =
|
||
state.user.apps.addUser;
|
||
|
||
// 必須項目のチェック
|
||
const hasErrorEmptyName = name === "";
|
||
const hasErrorEmptyEmail = email === "";
|
||
// Authorの場合、AuthorIDが必須(空文字,undefinedは不可)
|
||
const hasErrorEmptyAuthorId =
|
||
role === USER_ROLES.AUTHOR && (authorId === "" || !authorId);
|
||
|
||
const hasErrorIncorrectAuthorId = checkErrorIncorrectAuthorId(
|
||
authorId ?? undefined,
|
||
role
|
||
);
|
||
|
||
const hasErrorIncorrectEmail = email.match(/^[^@]+@[^@]+$/)?.length !== 1;
|
||
|
||
const hasErrorIncorrectEncryptionPassword =
|
||
checkErrorIncorrectEncryptionPassword(encryptionPassword, role, encryption);
|
||
|
||
return {
|
||
hasErrorEmptyName,
|
||
hasErrorEmptyEmail,
|
||
hasErrorEmptyAuthorId,
|
||
hasErrorIncorrectEmail,
|
||
hasErrorIncorrectAuthorId,
|
||
hasErrorIncorrectEncryptionPassword,
|
||
};
|
||
};
|
||
|
||
export const selectUpdateValidationErrors = (state: RootState) => {
|
||
const { role, authorId, encryption, encryptionPassword } =
|
||
state.user.apps.updateUser;
|
||
const { encryption: initEncryption } = state.user.apps.selectedUser;
|
||
|
||
// Authorの場合、AuthorIDが必須(空文字,undefinedは不可)
|
||
const hasErrorEmptyAuthorId =
|
||
role === USER_ROLES.AUTHOR && (authorId === "" || !authorId);
|
||
|
||
const hasErrorIncorrectAuthorId = checkErrorIncorrectAuthorId(
|
||
authorId ?? undefined,
|
||
role
|
||
);
|
||
|
||
let hasErrorIncorrectEncryptionPassword = false;
|
||
|
||
const passwordError = checkErrorIncorrectEncryptionPassword(
|
||
encryptionPassword,
|
||
role,
|
||
encryption
|
||
);
|
||
|
||
if (passwordError) {
|
||
// 最初にEncryptionがfasleで、Encryptionがtrueに変更された場合、EncryptionPasswordが必須
|
||
if (!initEncryption) {
|
||
hasErrorIncorrectEncryptionPassword = true;
|
||
// Encryptionがある状態で変更がある場合、EncryptionPasswordが空でもエラーにしない
|
||
} else if (!encryptionPassword || encryptionPassword === "") {
|
||
hasErrorIncorrectEncryptionPassword = false;
|
||
} else {
|
||
hasErrorIncorrectEncryptionPassword = true;
|
||
}
|
||
}
|
||
|
||
return {
|
||
hasErrorEmptyAuthorId,
|
||
hasErrorIncorrectAuthorId,
|
||
hasErrorIncorrectEncryptionPassword,
|
||
};
|
||
};
|
||
|
||
// encreyptionPasswordのチェック
|
||
const checkErrorIncorrectEncryptionPassword = (
|
||
encryptionPassword: string | undefined,
|
||
role: RoleType,
|
||
encryption: boolean | undefined
|
||
): boolean => {
|
||
// roleがAuthor以外の場合、チェックしない
|
||
if (role !== USER_ROLES.AUTHOR) {
|
||
return false;
|
||
}
|
||
// roleがAuthorかつencryptionがfalseの場合、チェックしない
|
||
if (!encryption) {
|
||
return false;
|
||
}
|
||
// encryptionPasswordがundefined,空文字の場合、エラー
|
||
if (!encryptionPassword || encryptionPassword === "") {
|
||
return true;
|
||
}
|
||
// encryptionPasswordがルールに則していない場合、エラー
|
||
const regex = /^[!-~]{4,16}$/;
|
||
if (!regex.test(encryptionPassword)) {
|
||
return true;
|
||
}
|
||
// チェックを通ったらエラーではない
|
||
return false;
|
||
};
|
||
|
||
export const checkErrorIncorrectAuthorId = (
|
||
authorId: string | undefined,
|
||
role: string
|
||
): boolean => {
|
||
if (!authorId || role !== USER_ROLES.AUTHOR) {
|
||
return false;
|
||
}
|
||
|
||
// 半角英数字と_の組み合わせで16文字まで
|
||
const charaTypePattern = /^[A-Z0-9_]{1,16}$/;
|
||
const charaType = new RegExp(charaTypePattern).test(authorId);
|
||
|
||
return !charaType;
|
||
};
|
||
|
||
export const selectName = (state: RootState) => state.user.apps.addUser.name;
|
||
export const selectEmail = (state: RootState) => state.user.apps.addUser.email;
|
||
export const selectRole = (state: RootState) => state.user.apps.addUser.role;
|
||
export const selectAuthorId = (state: RootState) =>
|
||
state.user.apps.addUser.authorId;
|
||
export const selectAutoRenew = (state: RootState) =>
|
||
state.user.apps.addUser.autoRenew;
|
||
export const selectLicenseAlert = (state: RootState) =>
|
||
state.user.apps.addUser.licenseAlert;
|
||
export const selectNotification = (state: RootState) =>
|
||
state.user.apps.addUser.notification;
|
||
// AddUserを返却する
|
||
export const selectAddUser = (state: RootState): AddUser =>
|
||
state.user.apps.addUser;
|
||
// usersからUserViewに変換して返却する
|
||
export const selectUserViews = (state: RootState): UserView[] => {
|
||
const { users } = state.user.domain;
|
||
const userViews = users.map((user): UserView => {
|
||
const {
|
||
role,
|
||
authorId,
|
||
encryption,
|
||
prompt,
|
||
typistGroupName,
|
||
licenseStatus,
|
||
expiration,
|
||
remaining,
|
||
...rest
|
||
} = user;
|
||
// roleの型がstringなので、isRoleTypeで型ガードを行う
|
||
// roleの型がRoleTypeでなければ、何も返さない
|
||
if (!isRoleType(role) || !isLicenseStatusType(licenseStatus)) {
|
||
return {} as UserView;
|
||
}
|
||
const convertedValues = convertValueBasedOnRole(
|
||
role,
|
||
authorId,
|
||
encryption,
|
||
prompt,
|
||
typistGroupName
|
||
);
|
||
// restのid以外をUserViewに追加する
|
||
return {
|
||
typistGroupName: convertedValues.typistGroupName,
|
||
prompt: convertedValues.prompt,
|
||
encryption: convertedValues.encryption,
|
||
authorId: convertedValues.authorId,
|
||
// roleの一文字目を大文字に変換する
|
||
role: role.charAt(0).toUpperCase() + role.slice(1),
|
||
licenseStatus:
|
||
licenseStatus === LICENSE_STATUS.NORMAL ? "-" : licenseStatus,
|
||
expiration: expiration ?? "-",
|
||
remaining: remaining ?? "-",
|
||
...rest,
|
||
};
|
||
});
|
||
// 空のオブジェクトを除外する
|
||
return userViews.filter((userView) => Object.keys(userView).length !== 0);
|
||
};
|
||
|
||
export const selectIsLoading = (state: RootState) => state.user.apps.isLoading;
|
||
|
||
// roleに応じて値を変換する
|
||
const convertValueBasedOnRole = (
|
||
role: RoleType,
|
||
authorId: string | undefined,
|
||
encryption: boolean,
|
||
prompt: boolean,
|
||
typistGroupName: string[]
|
||
): {
|
||
authorId: string;
|
||
encryption: boolean | string;
|
||
prompt: boolean | string;
|
||
typistGroupName: string[] | string;
|
||
} => {
|
||
if (role === USER_ROLES.AUTHOR && authorId) {
|
||
return {
|
||
authorId,
|
||
encryption,
|
||
prompt,
|
||
typistGroupName: "-",
|
||
};
|
||
}
|
||
if (role === USER_ROLES.TYPIST) {
|
||
return {
|
||
authorId: "-",
|
||
encryption: "-",
|
||
prompt: "-",
|
||
typistGroupName,
|
||
};
|
||
}
|
||
return {
|
||
authorId: "-",
|
||
encryption: "-",
|
||
prompt: "-",
|
||
typistGroupName: "-",
|
||
};
|
||
};
|
||
|
||
export const selectUpdateUser = (state: RootState) =>
|
||
state.user.apps.updateUser;
|
||
|
||
export const selectHasPasswordMask = (state: RootState) =>
|
||
state.user.apps.hasPasswordMask;
|