Merged PR 700: [テストFB対応]User画面でライセンスのStatusの表示を変更したい

## 概要
[Task3506: テスト対応](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3506)

- ライセンスが割り当たっている状態の表示をLicense Assignedにする
- ライセンスの期限切れ状態の表示をNo Licenseとする
- ヘッダーのOMDSCloudの表記を削除

## レビューポイント
- 修正内容に不足はないか
- 修正の認識ずれはないか
- ほかに修正が影響している箇所はないか

## UIの変更
- 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/Task3506?csf=1&web=1&e=g2Hkr3

## 動作確認状況
- ローカルで確認

## 補足
- 相談、参考資料などがあれば
This commit is contained in:
saito.k 2024-01-23 11:02:50 +00:00
parent cf56239da2
commit a59cb0fffb
16 changed files with 245 additions and 213 deletions

View File

@ -51,8 +51,6 @@ export const HEADER_MENUS: {
},
];
export const HEADER_NAME = "ODMS Cloud";
/**
* adminのみに表示するヘッダータブ
*/

View File

@ -17,7 +17,6 @@ import { getFilteredMenus } from "./utils";
import logo from "../../assets/images/OMS_logo_black.svg";
import ac from "../../assets/images/account_circle.svg";
import { LoginedPaths } from "./types";
import { HEADER_NAME } from "./constants";
import logout from "../../assets/images/logout.svg";
import { getTranslationID } from "../../translation";
@ -74,7 +73,6 @@ const LoginedHeader: React.FC<HeaderProps> = (props: HeaderProps) => {
<div className={styles.headerLogo}>
<img src={logo} alt="OM System" />
</div>
<div className={styles.headerSub}>{HEADER_NAME}</div>
<div className={styles.headerMenu}>
<ul>
{filterMenus.map((x) => (

View File

@ -1,7 +1,6 @@
import React from "react";
import styles from "styles/app.module.scss";
import logo from "../../assets/images/OMS_logo_black.svg";
import { HEADER_NAME } from "./constants";
interface NotLoginHeaderProps {
isMobile?: boolean;
@ -16,7 +15,6 @@ const NotLoginHeader: React.FC<NotLoginHeaderProps> = (
<div className={`${styles.headerLogo}`}>
<img src={logo} alt="OM System" />
</div>
<p className={`${styles.headerSub}`}>{HEADER_NAME}</p>
</header>
);
};

View File

@ -11,3 +11,9 @@ export const LICENSE_ALLOCATE_STATUS = {
ALLOCATED: "Allocated",
NOTALLOCATED: "Not Allocated",
} as const;
// NoLicenseの表示
export const NO_LICENSE = "No License" as const;
// ライセンスが割り当てられている場合の表示
export const LICENSE_NORMAL = "License Assigned" as const;

View File

@ -3,6 +3,7 @@ import { USER_ROLES } from "components/auth/constants";
import { convertLocalToUTCDate } from "common/convertLocalToUTCDate";
import {
AddUser,
LicenseStatusType,
RoleType,
UserView,
isLicenseStatusType,
@ -163,6 +164,12 @@ export const selectUserViews = (state: RootState): UserView[] => {
prompt,
typistGroupName
);
// licenseStatus,remaining,expirationの値を変換する
const {
licenseStatus: convertedLicenseStatus,
expiration: convertedExpiration,
remaining: convertedRemaining,
} = convertValueBasedOnLicenseStatus(licenseStatus, expiration, remaining);
// restのid以外をUserViewに追加する
return {
typistGroupName: convertedValues.typistGroupName,
@ -171,10 +178,9 @@ export const selectUserViews = (state: RootState): UserView[] => {
authorId: convertedValues.authorId,
// roleの一文字目を大文字に変換する
role: role.charAt(0).toUpperCase() + role.slice(1),
licenseStatus:
licenseStatus === LICENSE_STATUS.NORMAL ? "-" : licenseStatus,
expiration: expiration ?? "-",
remaining: remaining ?? "-",
licenseStatus: convertedLicenseStatus,
expiration: convertedExpiration,
remaining: convertedRemaining,
...rest,
};
});
@ -254,7 +260,7 @@ export const selectLicenseAllocateUserExpirationDate = (state: RootState) => {
return "-";
}
return `${expiration}(${remaining})`;
return `${expiration ?? "-"}(${remaining ?? "-"})`;
};
export const selectSelectedlicenseId = (state: RootState) =>
@ -328,3 +334,47 @@ const calculateExpiryDate = (expiryDate: string) => {
return `${formattedExpirationDate} (${daysDifference})`;
};
// licenseStatus,remainingに応じて値を変換する
const convertValueBasedOnLicenseStatus = (
licenseStatus: LicenseStatusType,
expiration?: string,
remaining?: number
): {
licenseStatus: LicenseStatusType;
expiration?: string;
remaining?: number;
} => {
if (licenseStatus === LICENSE_STATUS.NOLICENSE) {
return {
licenseStatus,
expiration: undefined,
remaining: undefined,
};
}
// remainingが存在し、かつ負の値である場合は、NoLicenseとする
if (remaining && remaining < 0) {
return {
licenseStatus: LICENSE_STATUS.NOLICENSE,
expiration: undefined,
remaining: undefined,
};
}
if (
licenseStatus === LICENSE_STATUS.RENEW ||
licenseStatus === LICENSE_STATUS.NORMAL ||
licenseStatus === LICENSE_STATUS.ALERT
) {
return {
licenseStatus,
expiration,
remaining,
};
}
// ここに到達することはない
return {
licenseStatus,
expiration: undefined,
remaining: undefined,
};
};

View File

@ -4,14 +4,11 @@ import { LICENSE_STATUS } from "./constants";
// 画面表示用のUserの型を独自に定義
export interface UserView
extends Omit<
User,
"typistGroupName" | "prompt" | "encryption" | "remaining"
> {
extends Omit<User, "typistGroupName" | "prompt" | "encryption"> {
authorId: string;
typistGroupName: string[] | string;
role: string;
licenseStatus: LicenseStatusType | string;
licenseStatus: LicenseStatusType;
prompt: boolean | string;
encryption: boolean | string;
emailVerified: boolean;
@ -19,8 +16,8 @@ export interface UserView
notification: boolean;
name: string;
email: string;
expiration: string;
remaining: number | string;
expiration?: string;
remaining?: number;
}
export interface AddUser {
name: string;
@ -53,8 +50,8 @@ export interface LicenseAllocateUser {
email: string;
authorId: string;
licenseStatus: LicenseStatusType | string;
expiration: string;
remaining: number | string;
expiration?: string;
remaining?: number;
}
export type RoleType = typeof USER_ROLES[keyof typeof USER_ROLES];

View File

@ -54,8 +54,8 @@ const initialState: UsersState = {
email: "",
authorId: "",
licenseStatus: "",
expiration: "",
remaining: "",
expiration: undefined,
remaining: undefined,
},
selectedlicenseId: 0,
hasPasswordMask: false,

View File

@ -13,8 +13,12 @@ import {
} from "features/user";
import { useTranslation } from "react-i18next";
import { getTranslationID } from "translation";
import { isLicenseStatusType, UserView } from "features/user/types";
import { LICENSE_STATUS } from "features/user/constants";
import { LicenseStatusType, UserView } from "features/user/types";
import {
LICENSE_NORMAL,
LICENSE_STATUS,
NO_LICENSE,
} from "features/user/constants";
import { isApproveTier } from "features/auth";
import { TIERS } from "components/auth/constants";
import {
@ -184,66 +188,63 @@ const UserListPage: React.FC = (): JSX.Element => {
</th>
</tr>
{!isLoading &&
users.map((user) => {
const { isAlertLicenseStatus, isAlertRemaining } =
isAlertElement(user.licenseStatus);
return (
<tr key={user.email}>
<td className={styles.clm0}>
<ul className={styles.menuInTable}>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
<a
onClick={() => {
onUpdateOpen(user.id);
}}
>
{t(
getTranslationID(
"userListPage.label.editUser"
)
)}
</a>
</li>
{isTier5 && (
<>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
<a
onClick={() => {
onAllocateLicensePopupOpen(user);
}}
>
{t(
getTranslationID(
"userListPage.label.licenseAllocation"
)
)}
</a>
</li>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
<a
className={
user.licenseStatus ===
LICENSE_STATUS.NOLICENSE
? styles.isDisable
: ""
}
onClick={() => {
onLicenseDeallocation(user.id);
}}
>
{t(
getTranslationID(
"userListPage.label.licenseDeallocation"
)
)}
</a>
</li>
</>
)}
{/* CCB
users.map((user) => (
<tr key={user.email}>
<td className={styles.clm0}>
<ul className={styles.menuInTable}>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
<a
onClick={() => {
onUpdateOpen(user.id);
}}
>
{t(
getTranslationID(
"userListPage.label.editUser"
)
)}
</a>
</li>
{isTier5 && (
<>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
<a
onClick={() => {
onAllocateLicensePopupOpen(user);
}}
>
{t(
getTranslationID(
"userListPage.label.licenseAllocation"
)
)}
</a>
</li>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
<a
className={
user.licenseStatus ===
LICENSE_STATUS.NOLICENSE
? styles.isDisable
: ""
}
onClick={() => {
onLicenseDeallocation(user.id);
}}
>
{t(
getTranslationID(
"userListPage.label.licenseDeallocation"
)
)}
</a>
</li>
</>
)}
{/* CCB
<li>
<a href="">
{t(
@ -254,48 +255,55 @@ const UserListPage: React.FC = (): JSX.Element => {
</a>
</li>
*/}
</ul>
</td>
<td> {user.name}</td>
<td>{user.role}</td>
<td>{user.authorId}</td>
<td>{boolToElement(user.encryption)}</td>
<td>{boolToElement(user.prompt)}</td>
<td>{arrayToElement(user.typistGroupName)}</td>
<td>{user.email}</td>
<td>
<span
className={
isAlertLicenseStatus ? styles.isAlert : ""
}
>
{user.licenseStatus}
</span>
</td>
<td>
<span
className={
isAlertRemaining ? styles.isAlert : ""
}
>
{user.expiration}
</span>
</td>
<td>
<span
className={
isAlertRemaining ? styles.isAlert : ""
}
>
{user.remaining}
</span>
</td>
<td>{boolToElement(user.autoRenew)}</td>
<td>{boolToElement(user.notification)}</td>
<td>{boolToElement(user.emailVerified)}</td>
</tr>
);
})}
</ul>
</td>
<td> {user.name}</td>
<td>{user.role}</td>
<td>{user.authorId}</td>
<td>{boolToElement(user.encryption)}</td>
<td>{boolToElement(user.prompt)}</td>
<td>{arrayToElement(user.typistGroupName)}</td>
<td>{user.email}</td>
<td>
<span
className={
user.licenseStatus ===
LICENSE_STATUS.NOLICENSE ||
user.licenseStatus === LICENSE_STATUS.ALERT
? styles.isAlert
: ""
}
>
{getLicenseStatus(user.licenseStatus)}
</span>
</td>
<td>
<span
className={
user.licenseStatus === LICENSE_STATUS.ALERT
? styles.isAlert
: ""
}
>
{user.expiration ?? "-"}
</span>
</td>
<td>
<span
className={
user.licenseStatus === LICENSE_STATUS.ALERT
? styles.isAlert
: ""
}
>
{user.remaining ?? "-"}
</span>
</td>
<td>{boolToElement(user.autoRenew)}</td>
<td>{boolToElement(user.notification)}</td>
<td>{boolToElement(user.emailVerified)}</td>
</tr>
))}
</tbody>
</table>
{!isLoading && users.length === 0 && (
@ -352,38 +360,15 @@ const arrayToElement = (
));
};
const isAlertElement = (
licenseStatus: string
): {
isAlertLicenseStatus: boolean;
isAlertRemaining: boolean;
} => {
// licenseStatusの型がLicenseStatusTypeでない場合(Normal)、どちらもfalseにする
if (isLicenseStatusType(licenseStatus) === false) {
return {
isAlertLicenseStatus: false,
isAlertRemaining: false,
};
}
// licenseStatusがNOLICENSEの場合、isAlertLicenseStatusをtrueにする(Remainingはハイフン)
// ライセンスステータスに応じて、ライセンスステータスの文字列を返す
const getLicenseStatus = (licenseStatus: LicenseStatusType): string => {
if (licenseStatus === LICENSE_STATUS.NOLICENSE) {
return {
isAlertLicenseStatus: true,
isAlertRemaining: false,
};
return NO_LICENSE;
}
// licenseStatusがALERTの場合、どちらもtrueにする
if (licenseStatus === LICENSE_STATUS.ALERT) {
return {
isAlertLicenseStatus: true,
isAlertRemaining: true,
};
if (licenseStatus === LICENSE_STATUS.NORMAL) {
return LICENSE_NORMAL;
}
// licenseStatusがRENEWの場合、どちらもfalseにする
return {
isAlertLicenseStatus: false,
isAlertRemaining: false,
};
return licenseStatus;
};
export default UserListPage;

View File

@ -1,4 +1,4 @@
import { DataSource } from "typeorm";
import { DataSource } from 'typeorm';
export const truncateAllTable = async (source: DataSource) => {
const entities = source.entityMetadatas;
@ -18,4 +18,4 @@ export const truncateAllTable = async (source: DataSource) => {
} finally {
await queryRunner.release();
}
};
};

View File

@ -340,7 +340,7 @@ describe('generateDelegationRefreshToken', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -486,7 +486,7 @@ describe('generateDelegationAccessToken', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -597,7 +597,7 @@ describe('updateDelegationAccessToken', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',

View File

@ -46,7 +46,7 @@ describe('publishUploadSas', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -302,7 +302,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -1024,7 +1024,7 @@ describe('音声ファイルダウンロードURL取得', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -1529,7 +1529,7 @@ describe('テンプレートファイルダウンロードURL取得', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -1984,7 +1984,7 @@ describe('publishTemplateFileUploadSas', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -2099,7 +2099,7 @@ describe('templateUploadFinished', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',

View File

@ -32,7 +32,7 @@ describe('ライセンス注文', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -205,7 +205,7 @@ describe('カードライセンス発行', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -295,7 +295,7 @@ describe('カードライセンスを取り込む', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -640,7 +640,7 @@ describe('ライセンス割り当て', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -1220,7 +1220,7 @@ describe('ライセンス割り当て解除', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -1389,7 +1389,7 @@ describe('ライセンス注文キャンセル', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',

View File

@ -586,33 +586,33 @@ describe('TasksService', () => {
describe('DBテスト', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
port: 3306,
username: 'user',
password: 'password',
database: 'odms',
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
synchronize: false, // trueにすると自動的にmigrationが行われるため注意
});
return await s.initialize();
})();
}
});
if (source == null) {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
port: 3306,
username: 'user',
password: 'password',
database: 'odms',
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
synchronize: false, // trueにすると自動的にmigrationが行われるため注意
});
return await s.initialize();
})();
}
});
beforeEach(async () => {
if (source) {
await truncateAllTable(source);
}
});
beforeEach(async () => {
if (source) {
await truncateAllTable(source);
}
});
afterAll(async () => {
await source?.destroy();
source = null;
});
afterAll(async () => {
await source?.destroy();
source = null;
});
it('[Admin] Taskが0件であっても実行できる', async () => {
const notificationhubServiceMockValue =
@ -790,7 +790,7 @@ describe('changeCheckoutPermission', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -1432,7 +1432,7 @@ describe('checkout', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -2026,7 +2026,7 @@ describe('checkin', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -2241,7 +2241,7 @@ describe('suspend', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -2452,7 +2452,7 @@ describe('cancel', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -3080,7 +3080,7 @@ describe('backup', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -3386,7 +3386,7 @@ describe('getNextTask', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',

View File

@ -12,7 +12,7 @@ describe('利用規約取得', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',

View File

@ -436,7 +436,7 @@ describe('UsersService.createUser', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -1393,7 +1393,7 @@ describe('UsersService.getUsers', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -1882,7 +1882,7 @@ describe('UsersService.updateUser', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -2478,7 +2478,7 @@ describe('UsersService.updateAcceptedVersion', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -2578,7 +2578,7 @@ describe('UsersService.getUserName', () => {
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -2632,7 +2632,7 @@ describe('UsersService.getRelations', () => {
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
@ -2708,7 +2708,7 @@ describe('UsersService.getRelations', () => {
{
const workflows = await getWorkflows(source, account.id);
workflows.sort((a, b) => a.id - b.id);
expect(workflows.length).toBe(4);
expect(workflows[0].worktype_id).toBe(worktype1.id);
expect(workflows[0].author_id).toBe(user1);

View File

@ -25,7 +25,7 @@ describe('getWorkflows', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await(async () => {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',