saito.k 7a453c80f8 Merged PR 372: 画面実装(WorktypeID設定画面)
## 概要
[Task2506: 画面実装(WorktypeID設定画面)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2506)

- WorktypeID設定画面を実装
  - WorktypeID一覧を表示する
  - 各種ボタンを表示する
  - ActiveWorktypeIDのセレクトボックスを表示する

- 言語切り替えの対応で同一の文言をcommonにまとめ、該当箇所を修正

## レビューポイント
- stateのdomain配下にあるAPIからの戻り値をOptionalにしたが問題ないか
  - 必須にすると画面初期表示時に「0件です」がちらつくため

## 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/Task2506?csf=1&web=1&e=IHIC1Y

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

## 補足
- Returnボタン以外のボタンの挙動はレビュー対象外
- Active WorkTypeIDの挙動はレビュー対象外
2023-09-01 07:27:36 +00:00

533 lines
20 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useCallback, useState, useEffect } from "react";
import Footer from "components/footer";
import Header from "components/header";
import styles from "styles/app.module.scss";
import { UpdateTokenTimer } from "components/auth/updateTokenTimer";
import { useDispatch, useSelector } from "react-redux";
import { AppDispatch } from "app/store";
import { getTranslationID } from "translation";
import { useTranslation } from "react-i18next";
import { PartnerLicenseInfo } from "api";
import { CardLicenseIssuePopup } from "./cardLicenseIssuePopup";
import postAdd from "../../assets/images/post_add.svg";
import history from "../../assets/images/history.svg";
import returnLabel from "../../assets/images/undo.svg";
import { isApproveTier } from "../../features/auth/utils";
import { TIERS } from "../../components/auth/constants";
import {
getPartnerLicenseAsync,
ACCOUNTS_VIEW_LIMIT,
selectMyAccountInfo,
selectTotal,
selectOwnPartnerLicense,
selectChildrenPartnerLicenses,
selectHierarchicalElements,
selectTotalPage,
selectIsLoading,
selectOffset,
selectCurrentPage,
pushHierarchicalElement,
popHierarchicalElement,
spliceHierarchicalElement,
savePageInfo,
getMyAccountAsync,
changeSelectedRow,
} from "../../features/license/partnerLicense";
import { LicenseOrderPopup } from "./licenseOrderPopup";
import { LicenseOrderHistory } from "./licenseOrderHistory";
import { LicenseSummary } from "./licenseSummary";
import progress_activit from "../../assets/images/progress_activit.svg";
const PartnerLicense: React.FC = (): JSX.Element => {
const dispatch: AppDispatch = useDispatch();
const [t] = useTranslation();
// popup制御関係
const [isCardLicenseIssuePopupOpen, setIsCardLicenseIssuePopupOpen] =
useState(false);
const [islicenseOrderPopupOpen, setIslicenseOrderPopupOpen] = useState(false);
const [islicenseOrderHistoryOpen, setIslicenseOrderHistoryOpen] =
useState(false);
const [isViewDetailsOpen, setIsViewDetailsOpen] = useState(false);
// 階層表示用
const tierNames: { [key: number]: string } = {
// eslint-disable-next-line @typescript-eslint/naming-convention
1: t(getTranslationID("common.label.tier1")),
// eslint-disable-next-line @typescript-eslint/naming-convention
2: t(getTranslationID("common.label.tier2")),
// eslint-disable-next-line @typescript-eslint/naming-convention
3: t(getTranslationID("common.label.tier3")),
// eslint-disable-next-line @typescript-eslint/naming-convention
4: t(getTranslationID("common.label.tier4")),
// eslint-disable-next-line @typescript-eslint/naming-convention
5: t(getTranslationID("common.label.tier5")),
};
const onlicenseIssueOpen = useCallback(() => {
setIsCardLicenseIssuePopupOpen(true);
}, [setIsCardLicenseIssuePopupOpen]);
const onlicenseOrderOpen = useCallback(() => {
setIslicenseOrderPopupOpen(true);
}, [setIslicenseOrderPopupOpen]);
// apiからの値取得関係
const myAccountInfo = useSelector(selectMyAccountInfo);
const total = useSelector(selectTotal);
const totalPage = useSelector(selectTotalPage);
const ownPartnerLicenseInfo = useSelector(selectOwnPartnerLicense);
const childrenPartnerLicensesInfo = useSelector(
selectChildrenPartnerLicenses
);
const hierarchicalElements = useSelector(selectHierarchicalElements);
const isLoading = useSelector(selectIsLoading);
// ページネーション制御用
const currentPage = useSelector(selectCurrentPage);
const offset = useSelector(selectOffset);
// ページネーションのボタンクリック時のアクション
const movePage = (targetOffset: number) => {
dispatch(
savePageInfo({ limit: ACCOUNTS_VIEW_LIMIT, offset: targetOffset })
);
};
// パンくずリスト内のアカウント押下時
const onClickBreadCrumbList = (id: number) => {
const clickLevel = hierarchicalElements.findIndex(
(param) => param.accountId === id
);
const nowLevel = hierarchicalElements.length - 1;
const deleteCount = nowLevel - clickLevel;
// 階層を上がった分だけ表示アカウント管理用の配列の最後からパラメータを削除する
if (deleteCount > 0) {
dispatch(spliceHierarchicalElement({ deleteCount }));
movePage(0);
}
};
// 行要素クリック時イベント
const handleRowClick = (value: PartnerLicenseInfo) => {
dispatch(
pushHierarchicalElement({
hierarchicalElement: {
accountId: value.accountId,
companyName: value.companyName,
},
})
);
movePage(0);
};
// returnボタンクリック時イベント
const returnClick = () => {
dispatch(popHierarchicalElement());
movePage(0);
};
// ログイン時に階層をチェック
const isTier1 = isApproveTier([TIERS.TIER1]);
const isTier2ToTier4 = isApproveTier([TIERS.TIER2, TIERS.TIER3, TIERS.TIER4]);
// viewDetailsボタン押下時
const onClickViewDetails = useCallback(
(value?: PartnerLicenseInfo) => {
dispatch(changeSelectedRow({ value }));
setIsViewDetailsOpen(true);
},
[dispatch, setIsViewDetailsOpen]
);
// orderHistoryボタン押下時
const onClickOrderHistory = useCallback(
(value?: PartnerLicenseInfo) => {
dispatch(changeSelectedRow({ value }));
setIslicenseOrderHistoryOpen(true);
},
[dispatch, setIslicenseOrderHistoryOpen]
);
// マウント時のみ実行
useEffect(() => {
dispatch(getMyAccountAsync());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// 自アカウントID取得時に実行
useEffect(() => {
if (myAccountInfo.accountId !== 0) {
dispatch(
getPartnerLicenseAsync({
limit: ACCOUNTS_VIEW_LIMIT,
offset,
accountId: myAccountInfo.accountId,
})
);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [myAccountInfo]);
// 現在の表示階層に合わせたボタン制御用
const [buttonLabel, setButtonLabel] = useState("");
// パンくずリスト用stateに自アカウントを追加
useEffect(() => {
if (
hierarchicalElements.length === 0 &&
ownPartnerLicenseInfo.accountId !== 0
) {
dispatch(
pushHierarchicalElement({
hierarchicalElement: {
accountId: ownPartnerLicenseInfo.accountId,
companyName: ownPartnerLicenseInfo.companyName,
},
})
);
}
// 表内のボタン表示判定
if (hierarchicalElements.length === 1 && ownPartnerLicenseInfo.tier !== 4) {
setButtonLabel(
t(getTranslationID("partnerLicense.label.orderHistoryButton"))
);
} else if (ownPartnerLicenseInfo.tier === 4) {
setButtonLabel(t(getTranslationID("partnerLicense.label.viewDetails")));
} else {
setButtonLabel("");
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ownPartnerLicenseInfo]);
// 表内の情報取得処理
useEffect(() => {
if (hierarchicalElements.length !== 0) {
dispatch(
getPartnerLicenseAsync({
limit: ACCOUNTS_VIEW_LIMIT,
offset,
accountId:
hierarchicalElements[hierarchicalElements.length - 1].accountId,
})
);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hierarchicalElements, currentPage]);
return (
<>
{/* 表示確認用の仮画面 */}
{/* isPopupOpenがfalseの場合はポップアップのhtmlを生成しないように対応。これによりポップアップは都度生成されて初期化の考慮が減る */}
{isCardLicenseIssuePopupOpen && (
<CardLicenseIssuePopup
onClose={() => {
setIsCardLicenseIssuePopupOpen(false);
dispatch(getMyAccountAsync());
}}
/>
)}
{islicenseOrderPopupOpen && (
<LicenseOrderPopup
onClose={() => {
setIslicenseOrderPopupOpen(false);
dispatch(getMyAccountAsync());
}}
/>
)}
{islicenseOrderHistoryOpen && (
<LicenseOrderHistory
onReturn={() => {
setIslicenseOrderHistoryOpen(false);
}}
/>
)}
{isViewDetailsOpen && (
<LicenseSummary
onReturn={() => {
setIsViewDetailsOpen(false);
}}
/>
)}
{!islicenseOrderHistoryOpen && !isViewDetailsOpen && (
<div className={styles.wrap}>
<Header userName="XXXXXX" />
<UpdateTokenTimer />
<main className={styles.main}>
<div className="">
<div className={styles.pageHeader}>
<h1 className={styles.pageTitle}>
{t(getTranslationID("partnerLicense.label.title"))}
</h1>
</div>
</div>
<section className={styles.license}>
<div>
<h2>{t(getTranslationID("partnerLicense.label.subTitle"))}</h2>
<ul className={styles.menuAction}>
<li>
{hierarchicalElements.length > 1 && (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<a
className={`${styles.menuLink} ${styles.isActive}`}
onClick={returnClick}
>
<img
src={returnLabel}
alt=""
className={styles.menuIcon}
/>
{t(getTranslationID("common.label.return"))}
</a>
)}
</li>
<li>
{isTier2ToTier4 && (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<a
className={`${styles.menuLink} ${styles.isActive}`}
onClick={onlicenseOrderOpen}
>
<img src={postAdd} alt="" className={styles.menuIcon} />
{t(
getTranslationID(
"partnerLicense.label.orderLicenseButton"
)
)}
</a>
)}
</li>
<li>
{isTier2ToTier4 && (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<a
className={`${styles.menuLink} ${styles.isActive}`}
onClick={() => {
onClickOrderHistory();
}}
>
<img src={history} alt="" className={styles.menuIcon} />
{t(
getTranslationID(
"partnerLicense.label.orderHistoryButton"
)
)}
</a>
)}
</li>
{/* 第1階層 */}
<li>
{isTier1 && (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<a
className={`${styles.menuLink} ${styles.isActive}`}
onClick={onlicenseIssueOpen}
>
<img src={postAdd} alt="" className={styles.menuIcon} />
{t(
getTranslationID(
"partnerLicense.label.IssueLicenseCardButton"
)
)}
</a>
)}
</li>
</ul>
<ul className={styles.brCrumbLicense}>
{hierarchicalElements.map((value) => (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions
<li
key={value.accountId}
onClick={() => {
onClickBreadCrumbList(value.accountId);
}}
>
<a>{value.companyName}</a>
</li>
))}
</ul>
<table className={`${styles.table} ${styles.partner}`}>
<tr className={styles.tableHeader}>
<th>{t(getTranslationID("partnerLicense.label.name"))}</th>
<th>
{t(getTranslationID("partnerLicense.label.category"))}
</th>
<th>
{t(getTranslationID("partnerLicense.label.accountId"))}
</th>
<th>
{t(getTranslationID("partnerLicense.label.stockLicense"))}
</th>
<th>
{t(
getTranslationID("partnerLicense.label.issueRequested")
)}
</th>
<th>
{t(getTranslationID("partnerLicense.label.shortage"))}
</th>
<th className={styles.noLine}>
{t(
getTranslationID("partnerLicense.label.issueRequesting")
)}
</th>
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
<th />
</tr>
<tr className={styles.isOpen}>
<td>{ownPartnerLicenseInfo.companyName}</td>
<td>{tierNames[ownPartnerLicenseInfo.tier]}</td>
<td>{ownPartnerLicenseInfo.accountId}</td>
<td>
{ownPartnerLicenseInfo.tier !== 1
? ownPartnerLicenseInfo.stockLicense
: "-"}
</td>
<td>{ownPartnerLicenseInfo.issuedRequested}</td>
<td>
{ownPartnerLicenseInfo.tier !== 1
? ownPartnerLicenseInfo.shortage
: "-"}
</td>
<td>
{ownPartnerLicenseInfo.tier !== 1
? ownPartnerLicenseInfo.issueRequesting
: "-"}
</td>
<td>
<ul
className={`${styles.menuAction} ${styles.menuInTable}`}
>
<li>{/* レイアウト維持用 */}</li>
</ul>
</td>
</tr>
{childrenPartnerLicensesInfo.map((value) => (
<tr key={value.accountId}>
<td
title={value.tier !== 5 ? "View child accounts" : ""}
onClick={() => {
if (value.tier !== 5) {
handleRowClick(value);
}
}}
>
{value.companyName}
</td>
<td>{tierNames[value.tier]}</td>
<td>{value.accountId}</td>
<td>{value.stockLicense}</td>
<td>{value.issuedRequested}</td>
<td>
<span
className={value.shortage > 0 ? styles.isAlert : ""}
>
{value.shortage}
</span>
</td>
<td>{value.issueRequesting}</td>
<td>
<ul
className={`${styles.menuAction} ${styles.inTable}`}
>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
className={`${styles.menuLink} ${
buttonLabel ? styles.isActive : ""
}`}
onClick={() => {
if (ownPartnerLicenseInfo.tier === 4) {
onClickViewDetails(value);
} else {
onClickOrderHistory(value);
}
}}
>
{buttonLabel}
</a>
</li>
</ul>
</td>
</tr>
))}
</table>
{/* pagenation */}
<div className={styles.pagenation}>
<nav className={styles.pagenationNav}>
<span className={styles.pagenationTotal}>
{total}{" "}
{t(getTranslationID("partnerLicense.label.accounts"))}
</span>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
onClick={() => {
movePage(0);
}}
className={` ${
!isLoading && currentPage !== 1 ? styles.isActive : ""
}`}
>
«
</a>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
onClick={() => {
movePage((currentPage - 2) * ACCOUNTS_VIEW_LIMIT);
}}
className={`${
!isLoading && currentPage !== 1 ? styles.isActive : ""
}`}
>
</a>
{` ${total !== 0 ? currentPage : 0} of ${
total !== 0 ? totalPage : 0
} `}
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
onClick={() => {
movePage(currentPage * ACCOUNTS_VIEW_LIMIT);
}}
className={`${
!isLoading && currentPage < totalPage
? styles.isActive
: ""
}`}
>
</a>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
onClick={() => {
movePage((totalPage - 1) * ACCOUNTS_VIEW_LIMIT);
}}
className={` ${
!isLoading && currentPage < totalPage
? styles.isActive
: ""
}`}
>
»
</a>
</nav>
</div>
<img
style={{ display: isLoading ? "inline" : "none" }}
src={progress_activit}
className={styles.icLoading}
alt="Loading"
/>
</div>
</section>
</main>
<Footer />
</div>
)}
;
</>
);
};
export default PartnerLicense;