From 83e297cc9b651797b1bfe1ba9b703069453fa3ab Mon Sep 17 00:00:00 2001 From: Kentaro Fukunaga Date: Wed, 13 Mar 2024 07:41:25 +0000 Subject: [PATCH] =?UTF-8?q?Merged=20PR=20821:=20=E7=94=BB=E9=9D=A2?= =?UTF-8?q?=E5=AE=9F=E8=A3=85=EF=BC=88=E3=83=91=E3=83=BC=E3=83=88=E3=83=8A?= =?UTF-8?q?=E3=83=BC=E3=83=A9=E3=82=A4=E3=82=BB=E3=83=B3=E3=82=B9=E4=B8=80?= =?UTF-8?q?=E8=A6=A7=E7=94=BB=E9=9D=A2=EF=BC=86=E9=9A=8E=E5=B1=A4=E6=A7=8B?= =?UTF-8?q?=E9=80=A0=E5=A4=89=E6=9B=B4=E3=83=9D=E3=83=83=E3=83=97=E3=82=A2?= =?UTF-8?q?=E3=83=83=E3=83=97=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task3854: 画面実装(パートナーライセンス一覧画面&階層構造変更ポップアップ)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3854) - パートナーライセンス一覧に「Change Owner」ボタンを配置し、表示制御およびクリック時にポップアップ表示する処理の実装 - アカウント階層構造変更ポップアップの処理全体実装 - サーバー側のエラーコード定義 ## レビューポイント - 「一括」を表現するためのドロップダウンの構築や処理周りで改善点ないか(to:斎藤くん) - コンポーネントでの状態管理でお作法に違反しているところないか(to:斎藤くん) - 修正箇所がほかの機能に影響していないか - パートナーライセンス一覧の画面表示に何らか悪影響ないか?(to:ガンさん) ## 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/Task3854?csf=1&web=1&e=jBGQrR ## クエリの変更 - なし ## 動作確認状況 - ローカルで確認しました - 第一階層でログインしてかつ第三または第四視点での一覧を確認しているときにChangeOwnerボタンが表示される - ボタン押下すると、仕様通りにポップアップの表示が行われる - ポップアップにて入力項目に入力できる&バリデーション効いている - ポップアップにて実行ボタン押下するとAPI実行できる&処理結果に応じて仕様通りの挙動をすること - 行った修正がデグレを発生させていないことを確認できるか - 具体的にどのような確認をしたか - パートナーライセンス画面に新規ボタンを配置した&新規ポップアップの実装のみのため、 ポップアップでの処理が正常終了/失敗/何もせず閉じた場合に元の画面の表示が今まで通り動くことを確認済み。 --- dictation_client/src/api/api.ts | 94 ++++++++ .../src/assets/images/change_circle.svg | 18 ++ .../src/assets/images/shuffle.svg | 1 + dictation_client/src/common/errors/code.ts | 4 + .../license/partnerLicense/operations.ts | 85 ++++++++ .../partnerLicense/partnerLicenseSlice.ts | 15 +- .../pages/LicensePage/changeOwnerPopup.tsx | 203 ++++++++++++++++++ .../src/pages/LicensePage/partnerLicense.tsx | 41 ++++ dictation_client/src/styles/app.module.scss | 55 +++++ .../src/styles/app.module.scss.d.ts | 7 + dictation_client/src/translation/de.json | 17 +- dictation_client/src/translation/en.json | 17 +- dictation_client/src/translation/es.json | 17 +- dictation_client/src/translation/fr.json | 17 +- dictation_server/src/common/error/code.ts | 4 + dictation_server/src/common/error/message.ts | 4 + 16 files changed, 594 insertions(+), 5 deletions(-) create mode 100644 dictation_client/src/assets/images/change_circle.svg create mode 100644 dictation_client/src/assets/images/shuffle.svg create mode 100644 dictation_client/src/pages/LicensePage/changeOwnerPopup.tsx diff --git a/dictation_client/src/api/api.ts b/dictation_client/src/api/api.ts index b68f729..28ca9ca 100644 --- a/dictation_client/src/api/api.ts +++ b/dictation_client/src/api/api.ts @@ -1840,6 +1840,25 @@ export interface SignupRequest { */ 'prompt'?: boolean; } +/** + * + * @export + * @interface SwitchParentRequest + */ +export interface SwitchParentRequest { + /** + * 切り替え先の親アカウントID + * @type {number} + * @memberof SwitchParentRequest + */ + 'to': number; + /** + * 親を変更したいアカウントIDのリスト + * @type {Array} + * @memberof SwitchParentRequest + */ + 'children': Array; +} /** * * @export @@ -3474,6 +3493,46 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat options: localVarRequestOptions, }; }, + /** + * + * @summary + * @param {SwitchParentRequest} switchParentRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + switchParent: async (switchParentRequest: SwitchParentRequest, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'switchParentRequest' is not null or undefined + assertParamExists('switchParent', 'switchParentRequest', switchParentRequest) + const localVarPath = `/accounts/parent/switch`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication bearer required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(switchParentRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, /** * * @summary @@ -4043,6 +4102,19 @@ export const AccountsApiFp = function(configuration?: Configuration) { const operationBasePath = operationServerMap['AccountsApi.issueLicense']?.[index]?.url; return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); }, + /** + * + * @summary + * @param {SwitchParentRequest} switchParentRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async switchParent(switchParentRequest: SwitchParentRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.switchParent(switchParentRequest, options); + const index = configuration?.serverIndex ?? 0; + const operationBasePath = operationServerMap['AccountsApi.switchParent']?.[index]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath); + }, /** * * @summary @@ -4369,6 +4441,16 @@ export const AccountsApiFactory = function (configuration?: Configuration, baseP issueLicense(issueLicenseRequest: IssueLicenseRequest, options?: any): AxiosPromise { return localVarFp.issueLicense(issueLicenseRequest, options).then((request) => request(axios, basePath)); }, + /** + * + * @summary + * @param {SwitchParentRequest} switchParentRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + switchParent(switchParentRequest: SwitchParentRequest, options?: any): AxiosPromise { + return localVarFp.switchParent(switchParentRequest, options).then((request) => request(axios, basePath)); + }, /** * * @summary @@ -4725,6 +4807,18 @@ export class AccountsApi extends BaseAPI { return AccountsApiFp(this.configuration).issueLicense(issueLicenseRequest, options).then((request) => request(this.axios, this.basePath)); } + /** + * + * @summary + * @param {SwitchParentRequest} switchParentRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof AccountsApi + */ + public switchParent(switchParentRequest: SwitchParentRequest, options?: AxiosRequestConfig) { + return AccountsApiFp(this.configuration).switchParent(switchParentRequest, options).then((request) => request(this.axios, this.basePath)); + } + /** * * @summary diff --git a/dictation_client/src/assets/images/change_circle.svg b/dictation_client/src/assets/images/change_circle.svg new file mode 100644 index 0000000..bf74c41 --- /dev/null +++ b/dictation_client/src/assets/images/change_circle.svg @@ -0,0 +1,18 @@ + + + + + + diff --git a/dictation_client/src/assets/images/shuffle.svg b/dictation_client/src/assets/images/shuffle.svg new file mode 100644 index 0000000..ac127a9 --- /dev/null +++ b/dictation_client/src/assets/images/shuffle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dictation_client/src/common/errors/code.ts b/dictation_client/src/common/errors/code.ts index ce0c7b6..9ba15db 100644 --- a/dictation_client/src/common/errors/code.ts +++ b/dictation_client/src/common/errors/code.ts @@ -77,4 +77,8 @@ export const errorCodes = [ "E016001", // テンプレートファイル削除エラー(削除しようとしたテンプレートファイルがすでに削除済みだった) "E016002", // テンプレートファイル削除エラー(削除しようとしたテンプレートファイルがWorkflowに指定されていた) "E016003", // テンプレートファイル削除エラー(削除しようとしたテンプレートファイルが未完了のタスクに紐づいていた) + "E017001", // 親アカウント変更不可エラー(指定したアカウントが存在しない) + "E017002", // 親アカウント変更不可エラー(階層関係が不正) + "E017003", // 親アカウント変更不可エラー(リージョンが同一でない) + "E017004", // 親アカウント変更不可エラー(国が同一でない) ] as const; diff --git a/dictation_client/src/features/license/partnerLicense/operations.ts b/dictation_client/src/features/license/partnerLicense/operations.ts index fbff656..8ceb0a8 100644 --- a/dictation_client/src/features/license/partnerLicense/operations.ts +++ b/dictation_client/src/features/license/partnerLicense/operations.ts @@ -105,3 +105,88 @@ export const getPartnerLicenseAsync = createAsyncThunk< return thunkApi.rejectWithValue({ error }); } }); + +export const switchParentAsync = createAsyncThunk< + { + /* Empty Object */ + }, + { + // パラメータ + to: number; + children: number[]; + }, + { + // rejectした時の返却値の型 + rejectValue: { + error: ErrorObject; + }; + } +>("accounts/switchParentAsync", async (args, thunkApi) => { + // apiのConfigurationを取得する + const { getState } = thunkApi; + const state = getState() as RootState; + const { configuration } = state.auth; + const accessToken = getAccessToken(state.auth); + const config = new Configuration(configuration); + const accountsApi = new AccountsApi(config); + + const { to, children } = args; + + try { + await accountsApi.switchParent( + { + to, + children, + }, + { + headers: { authorization: `Bearer ${accessToken}` }, + } + ); + thunkApi.dispatch( + openSnackbar({ + level: "info", + message: getTranslationID("common.message.success"), + }) + ); + return {}; + } catch (e) { + // e ⇒ errorObjectに変換" + const error = createErrorObject(e); + + let errorMessage = getTranslationID("common.message.internalServerError"); + + // TODO:エラー処理 + if (error.code === "E017001") { + errorMessage = getTranslationID( + "changeOwnerPopup.message.accountNotFoundError" + ); + } + + if (error.code === "E017002") { + errorMessage = getTranslationID( + "changeOwnerPopup.message.hierarchyMismatchError" + ); + } + + if (error.code === "E017003") { + errorMessage = getTranslationID( + "changeOwnerPopup.message.regionMismatchError" + ); + } + + if (error.code === "E017004") { + errorMessage = getTranslationID( + "changeOwnerPopup.message.countryMismatchError" + ); + } + + thunkApi.dispatch( + openSnackbar({ + level: "error", + message: errorMessage, + }) + ); + + return thunkApi.rejectWithValue({ error }); + } +}); diff --git a/dictation_client/src/features/license/partnerLicense/partnerLicenseSlice.ts b/dictation_client/src/features/license/partnerLicense/partnerLicenseSlice.ts index c6c8b92..b3c894b 100644 --- a/dictation_client/src/features/license/partnerLicense/partnerLicenseSlice.ts +++ b/dictation_client/src/features/license/partnerLicense/partnerLicenseSlice.ts @@ -1,7 +1,11 @@ import { PayloadAction, createSlice } from "@reduxjs/toolkit"; import { PartnerLicenseInfo } from "api"; import { PartnerLicensesState, HierarchicalElement } from "./state"; -import { getMyAccountAsync, getPartnerLicenseAsync } from "./operations"; +import { + getMyAccountAsync, + getPartnerLicenseAsync, + switchParentAsync, +} from "./operations"; import { ACCOUNTS_VIEW_LIMIT } from "./constants"; const initialState: PartnerLicensesState = { @@ -109,6 +113,15 @@ export const partnerLicenseSlice = createSlice({ builder.addCase(getPartnerLicenseAsync.rejected, (state) => { state.apps.isLoading = false; }); + builder.addCase(switchParentAsync.pending, (state) => { + state.apps.isLoading = true; + }); + builder.addCase(switchParentAsync.fulfilled, (state) => { + state.apps.isLoading = false; + }); + builder.addCase(switchParentAsync.rejected, (state) => { + state.apps.isLoading = false; + }); }, }); export const { diff --git a/dictation_client/src/pages/LicensePage/changeOwnerPopup.tsx b/dictation_client/src/pages/LicensePage/changeOwnerPopup.tsx new file mode 100644 index 0000000..04084f1 --- /dev/null +++ b/dictation_client/src/pages/LicensePage/changeOwnerPopup.tsx @@ -0,0 +1,203 @@ +import React, { useState, useCallback } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { + selectChildrenPartnerLicenses, + selectIsLoading, + selectOwnPartnerLicense, +} from "features/license/partnerLicense/selectors"; +import { + getMyAccountAsync, + switchParentAsync, +} from "features/license/partnerLicense/operations"; +import { useTranslation } from "react-i18next"; +import { getTranslationID } from "translation"; +import { AppDispatch } from "app/store"; +import { clearHierarchicalElement } from "features/license/partnerLicense"; +import styles from "../../styles/app.module.scss"; +import close from "../../assets/images/close.svg"; +import shuffle from "../../assets/images/shuffle.svg"; +import progress_activit from "../../assets/images/progress_activit.svg"; + +interface ChangeOwnerPopupProps { + onClose: () => void; +} + +const ChangeOwnerPopup: React.FC = (props) => { + const dispatch: AppDispatch = useDispatch(); + const { t } = useTranslation(); + + const [selectedChildId, setSelectedChildId] = useState(null); + const [selectedChildName, setSelectedChildName] = useState(""); + const [destinationParentId, setDestinationParentId] = useState(""); + const [error, setError] = useState(""); + + const originParentLicenseInfo = useSelector(selectOwnPartnerLicense); + const childrenLicenseInfos = useSelector(selectChildrenPartnerLicenses); + const isLoading = useSelector(selectIsLoading); + + const { onClose } = props; + const closePopup = useCallback(() => { + if (isLoading) return; + onClose(); + }, [isLoading, onClose]); + + const bulkDisplayName = "-- Bulk --"; + const bulkValue = "bulk"; + + const onBulkChange = useCallback( + (e: React.ChangeEvent) => { + const { value } = e.target; + const childId = value === bulkValue ? null : Number(value); + setSelectedChildId(childId); + + // 一括追加のときは子アカウント名を表示しない + let childName = ""; + if (childId) { + const child = childrenLicenseInfos.find((c) => c.accountId === childId); + // childがundefinedになることはないが、コード解析対応のためのチェック + if (child) { + childName = child.companyName; + } + } + setSelectedChildName(childName); + }, + [childrenLicenseInfos] + ); + + const onSaveClick = useCallback(async () => { + const destinationParentIdNum = Number(destinationParentId); + if ( + Number.isNaN(destinationParentIdNum) || // 数値でない場合 + destinationParentIdNum <= 0 || // IDにならない数値の場合 + destinationParentId.length > 7 // 8桁以上の場合(本システムの特徴として8桁以上になることはあり得ない) + ) { + setError(t(getTranslationID("changeOwnerPopup.label.invalidInputError"))); + return; + } + setError(""); + if ( + // eslint-disable-next-line no-alert + !window.confirm(t(getTranslationID("common.message.dialogConfirm"))) + ) { + return; + } + + const children = selectedChildId + ? [selectedChildId] + : childrenLicenseInfos.map((child) => child.accountId); + const { meta } = await dispatch( + switchParentAsync({ to: Number(destinationParentId), children }) + ); + if (meta.requestStatus === "fulfilled") { + dispatch(getMyAccountAsync()); + dispatch(clearHierarchicalElement()); + closePopup(); + } + }, [ + childrenLicenseInfos, + closePopup, + destinationParentId, + dispatch, + selectedChildId, + t, + ]); + + return ( +
+
+

+ {t(getTranslationID("changeOwnerPopup.label.title"))} + {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-noninteractive-element-interactions */} + close +

+
+
+
+
+ {t(getTranslationID("changeOwnerPopup.label.upperLayerId"))} +
+
+

+ + + {originParentLicenseInfo.companyName} + +

+

+

+ setDestinationParentId(e.target.value)} + /> + {error} +

+
+
+ +
+
+ {t(getTranslationID("changeOwnerPopup.label.lowerLayerId"))} +
+
+ + {selectedChildName} +
+
+ {/* 処理中や子アカウントが1件も存在しない場合、Saveボタンを押せないようにする */} + 0 + ? styles.isActive + : "" + }`} + onClick={onSaveClick} + disabled={isLoading || childrenLicenseInfos.length <= 0} + /> +
+ + Loading +
+
+
+
+ ); +}; + +export default ChangeOwnerPopup; diff --git a/dictation_client/src/pages/LicensePage/partnerLicense.tsx b/dictation_client/src/pages/LicensePage/partnerLicense.tsx index acc9801..03cf8aa 100644 --- a/dictation_client/src/pages/LicensePage/partnerLicense.tsx +++ b/dictation_client/src/pages/LicensePage/partnerLicense.tsx @@ -12,6 +12,7 @@ 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 changeOwnerIcon from "../../assets/images/change_circle.svg"; import { isApproveTier } from "../../features/auth/utils"; import { TIERS } from "../../components/auth/constants"; import { @@ -37,6 +38,7 @@ import { LicenseOrderPopup } from "./licenseOrderPopup"; import { LicenseOrderHistory } from "./licenseOrderHistory"; import { LicenseSummary } from "./licenseSummary"; import progress_activit from "../../assets/images/progress_activit.svg"; +import ChangeOwnerPopup from "./changeOwnerPopup"; const PartnerLicense: React.FC = (): JSX.Element => { const dispatch: AppDispatch = useDispatch(); @@ -49,6 +51,7 @@ const PartnerLicense: React.FC = (): JSX.Element => { const [islicenseOrderHistoryOpen, setIslicenseOrderHistoryOpen] = useState(false); const [isViewDetailsOpen, setIsViewDetailsOpen] = useState(false); + const [isChangeOwnerPopupOpen, setIsChangeOwnerPopupOpen] = useState(false); // 階層表示用 const tierNames: { [key: number]: string } = { @@ -148,6 +151,11 @@ const PartnerLicense: React.FC = (): JSX.Element => { [dispatch, setIslicenseOrderHistoryOpen] ); + // changeOwnerボタン押下時 + const onClickChangeOwner = useCallback(() => { + setIsChangeOwnerPopupOpen(true); + }, [setIsChangeOwnerPopupOpen]); + // マウント時のみ実行 useEffect(() => { dispatch(getMyAccountAsync()); @@ -245,6 +253,13 @@ const PartnerLicense: React.FC = (): JSX.Element => { }} /> )} + {isChangeOwnerPopupOpen && ( + { + setIsChangeOwnerPopupOpen(false); + }} + /> + )} {!islicenseOrderHistoryOpen && !isViewDetailsOpen && (
@@ -329,6 +344,26 @@ const PartnerLicense: React.FC = (): JSX.Element => { )} +
  • + {isVisibleChangeOwner(ownPartnerLicenseInfo.tier) && ( + // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions + + + {t( + getTranslationID( + "partnerLicense.label.changeOwnerButton" + ) + )} + + )} +
    • {hierarchicalElements.map((value) => ( @@ -545,4 +580,10 @@ const PartnerLicense: React.FC = (): JSX.Element => { ); }; +const isVisibleChangeOwner = (partnerTier: number) => + // 自身が第一階層または第二階層で、表示しているパートナーが第三階層または第四階層の場合のみ表示 + isApproveTier([TIERS.TIER1, TIERS.TIER2]) && + (partnerTier.toString() === TIERS.TIER3 || + partnerTier.toString() === TIERS.TIER4); + export default PartnerLicense; diff --git a/dictation_client/src/styles/app.module.scss b/dictation_client/src/styles/app.module.scss index c3a2d1e..36371a3 100644 --- a/dictation_client/src/styles/app.module.scss +++ b/dictation_client/src/styles/app.module.scss @@ -1999,6 +1999,61 @@ tr.isSelected .menuInTable li a.isDisable { text-align: right; } +.formList dd.ownerChange { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + margin-bottom: 0; +} +.formList dd.ownerChange p.Owner, +.formList dd.ownerChange p.newOwner { + width: 150px; +} +.formList dd.ownerChange .arrowR { + width: 8%; + height: 20px; + margin-top: 10px; + margin-right: 2%; + background: #e6e6e6; + position: relative; +} +.formList dd.ownerChange .arrowR::after { + content: ""; + border-top: 20px transparent solid; + border-bottom: 20px transparent solid; + border-left: 20px #e6e6e6 solid; + position: absolute; + top: 50%; + right: -15px; + transform: translateY(-50%); +} +.formList dd.ownerChange + .full { + width: 66%; + margin-left: 30%; + margin-bottom: -10px; + text-align: center; +} +.formList dd.ownerChange + .full .transOwner { + width: 100px; +} +.formList dd.lowerTrans { + margin-bottom: 1.5rem; + position: relative; + text-align: center; +} +.formList dd.lowerTrans select, +.formList dd.lowerTrans span { + margin: 0 auto; +} +.formList dd .txName { + display: block; + width: 150px; + padding: 0.2rem 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .dictation .menuAction { margin-top: -1rem; height: 34px; diff --git a/dictation_client/src/styles/app.module.scss.d.ts b/dictation_client/src/styles/app.module.scss.d.ts index e82c3c7..7f380fa 100644 --- a/dictation_client/src/styles/app.module.scss.d.ts +++ b/dictation_client/src/styles/app.module.scss.d.ts @@ -129,6 +129,13 @@ declare const classNames: { readonly cardHistory: "cardHistory"; readonly partner: "partner"; readonly isOpen: "isOpen"; + readonly ownerChange: "ownerChange"; + readonly Owner: "Owner"; + readonly newOwner: "newOwner"; + readonly arrowR: "arrowR"; + readonly transOwner: "transOwner"; + readonly lowerTrans: "lowerTrans"; + readonly txName: "txName"; readonly alignLeft: "alignLeft"; readonly displayOptions: "displayOptions"; readonly tableFilter: "tableFilter"; diff --git a/dictation_client/src/translation/de.json b/dictation_client/src/translation/de.json index 9a7af9d..6620775 100644 --- a/dictation_client/src/translation/de.json +++ b/dictation_client/src/translation/de.json @@ -368,7 +368,8 @@ "shortage": "Lizenzmangel", "issueRequesting": "Lizenzen auf Bestellung", "viewDetails": "Details anzeigen", - "accounts": "konten" + "accounts": "konten", + "changeOwnerButton": "(de)Change Owner" } }, "orderHistoriesPage": { @@ -619,5 +620,19 @@ "saveButton": "(de)Save Settings", "daysValidationError": "(de)Daysには1~999の数字を入力してください。" } + }, + "changeOwnerPopup": { + "message": { + "accountNotFoundError": "(de)変更先のアカウントIDは存在しません。", + "hierarchyMismatchError": "(de)パートナーアカウントの変更に失敗しました。\n子アカウントの1階層上のアカウントを切り替え先に指定してください。", + "regionMismatchError": "(de)パートナーアカウントの変更に失敗しました。\n子アカウントと同じリージョンのアカウントを切り替え先に指定してください。", + "countryMismatchError": "(de)パートナーアカウントの変更に失敗しました。\n子アカウントと同じ国のアカウントを切り替え先に指定してください。" + }, + "label": { + "invalidInputError": "(de)変更先アカウントIDには1~9999999の数字を入力してください。", + "title": "(de)Change Owner", + "upperLayerId": "(de)Upper Layer ID", + "lowerLayerId": "(de)Lower Layer ID" + } } } \ No newline at end of file diff --git a/dictation_client/src/translation/en.json b/dictation_client/src/translation/en.json index 7b91390..3151765 100644 --- a/dictation_client/src/translation/en.json +++ b/dictation_client/src/translation/en.json @@ -368,7 +368,8 @@ "shortage": "License Shortage", "issueRequesting": "Licenses on Order", "viewDetails": "View Details", - "accounts": "accounts" + "accounts": "accounts", + "changeOwnerButton": "Change Owner" } }, "orderHistoriesPage": { @@ -619,5 +620,19 @@ "saveButton": "Save Settings", "daysValidationError": "Daysには1~999の数字を入力してください。" } + }, + "changeOwnerPopup": { + "message": { + "accountNotFoundError": "変更先のアカウントIDは存在しません。", + "hierarchyMismatchError": "パートナーアカウントの変更に失敗しました。\n子アカウントの1階層上のアカウントを切り替え先に指定してください。", + "regionMismatchError": "パートナーアカウントの変更に失敗しました。\n子アカウントと同じリージョンのアカウントを切り替え先に指定してください。", + "countryMismatchError": "パートナーアカウントの変更に失敗しました。\n子アカウントと同じ国のアカウントを切り替え先に指定してください。" + }, + "label": { + "invalidInputError": "変更先アカウントIDには1~9999999の数字を入力してください。", + "title": "Change Owner", + "upperLayerId": "Upper Layer ID", + "lowerLayerId": "Lower Layer ID" + } } } \ No newline at end of file diff --git a/dictation_client/src/translation/es.json b/dictation_client/src/translation/es.json index 4dd9b29..a1bc703 100644 --- a/dictation_client/src/translation/es.json +++ b/dictation_client/src/translation/es.json @@ -368,7 +368,8 @@ "shortage": "Escasez de licencias", "issueRequesting": "Licencias en Pedido", "viewDetails": "Ver detalles", - "accounts": "cuentas" + "accounts": "cuentas", + "changeOwnerButton": "(es)Change Owner" } }, "orderHistoriesPage": { @@ -619,5 +620,19 @@ "saveButton": "(es)Save Settings", "daysValidationError": "(es)Daysには1~999の数字を入力してください。" } + }, + "changeOwnerPopup": { + "message": { + "accountNotFoundError": "(es)変更先のアカウントIDは存在しません。", + "hierarchyMismatchError": "(es)パートナーアカウントの変更に失敗しました。\n子アカウントの1階層上のアカウントを切り替え先に指定してください。", + "regionMismatchError": "(es)パートナーアカウントの変更に失敗しました。\n子アカウントと同じリージョンのアカウントを切り替え先に指定してください。", + "countryMismatchError": "(es)パートナーアカウントの変更に失敗しました。\n子アカウントと同じ国のアカウントを切り替え先に指定してください。" + }, + "label": { + "invalidInputError": "(es)変更先アカウントIDには1~9999999の数字を入力してください。", + "title": "(es)Change Owner", + "upperLayerId": "(es)Upper Layer ID", + "lowerLayerId": "(es)Lower Layer ID" + } } } \ No newline at end of file diff --git a/dictation_client/src/translation/fr.json b/dictation_client/src/translation/fr.json index af62dc2..d8b0555 100644 --- a/dictation_client/src/translation/fr.json +++ b/dictation_client/src/translation/fr.json @@ -368,7 +368,8 @@ "shortage": "Pénurie de licences", "issueRequesting": "Licences en commande", "viewDetails": "Voir les détails", - "accounts": "comptes" + "accounts": "comptes", + "changeOwnerButton": "(fr)Change Owner" } }, "orderHistoriesPage": { @@ -619,5 +620,19 @@ "saveButton": "(fr)Save Settings", "daysValidationError": "(fr)Daysには1~999の数字を入力してください。" } + }, + "changeOwnerPopup": { + "message": { + "accountNotFoundError": "(fr)変更先のアカウントIDは存在しません。", + "hierarchyMismatchError": "(fr)パートナーアカウントの変更に失敗しました。\n子アカウントの1階層上のアカウントを切り替え先に指定してください。", + "regionMismatchError": "(fr)パートナーアカウントの変更に失敗しました。\n子アカウントと同じリージョンのアカウントを切り替え先に指定してください。", + "countryMismatchError": "(fr)パートナーアカウントの変更に失敗しました。\n子アカウントと同じ国のアカウントを切り替え先に指定してください。" + }, + "label": { + "invalidInputError": "(fr)変更先アカウントIDには1~9999999の数字を入力してください。", + "title": "(fr)Change Owner", + "upperLayerId": "(fr)Upper Layer ID", + "lowerLayerId": "(fr)Lower Layer ID" + } } } \ No newline at end of file diff --git a/dictation_server/src/common/error/code.ts b/dictation_server/src/common/error/code.ts index a789ab7..84f6dd6 100644 --- a/dictation_server/src/common/error/code.ts +++ b/dictation_server/src/common/error/code.ts @@ -82,4 +82,8 @@ export const ErrorCodes = [ 'E016001', // テンプレートファイル削除エラー(削除しようとしたテンプレートファイルがすでに削除済みだった) 'E016002', // テンプレートファイル削除エラー(削除しようとしたテンプレートファイルがWorkflowに指定されていた) 'E016003', // テンプレートファイル削除エラー(削除しようとしたテンプレートファイルが未完了のタスクに紐づいていた) + 'E017001', // 親アカウント変更不可エラー(指定したアカウントが存在しない) + 'E017002', // 親アカウント変更不可エラー(階層関係が不正) + 'E017003', // 親アカウント変更不可エラー(リージョンが同一でない) + 'E017004', // 親アカウント変更不可エラー(国が同一でない) ] as const; diff --git a/dictation_server/src/common/error/message.ts b/dictation_server/src/common/error/message.ts index 793cdf5..2e22585 100644 --- a/dictation_server/src/common/error/message.ts +++ b/dictation_server/src/common/error/message.ts @@ -72,4 +72,8 @@ export const errors: Errors = { E016002: 'Template file delete failed Error: workflow assigned', E016003: 'Template file delete failed Error: not finished task has template file', + E017001: 'Parent account switch failed Error: account not found', + E017002: 'Parent account switch failed Error: hierarchy mismatch', + E017003: 'Parent account switch failed Error: region mismatch', + E017004: 'Parent account switch failed Error: country mismatch', };