Merged PR 531: 画面実装(代行操作)

## 概要
[Task2909: 画面実装(代行操作)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2909)

- ディーラーユーザーが代行操作用トークンを取得して、第五階層ユーザーの代行操作ができる実装をしました。
  - 代行後に各対象タブが代行操作デザインで表示され、代行対象として操作できるようになっています。
    - 各APIの呼び出しについて代行操作用トークンがあればそちらを使うように実装しています。
 - 代行操作のタブ、ページ移動時に代行操作を維持するために遷移をリンクから`useNavigate`に変更しました。

## レビューポイント
- 代行操作用トークンの取り扱いについて
  - APIからのトークン取得後、`store.auth`に代行操作用トークンを保存し、利用時には関数を使って間接的に呼でいますが構成として不自然な点はないでしょうか?
    - トークン取得関数では代行操作用トークンがあればそれを、なければ通常のトークンを取得するようにしています。
      - これはAPIのトークンを設定する際にトークンを取得側では代行操作中か意識せずに一つの関数を呼べばいいようにするためです。
  - 代行操作用トークンの保存は通常のトークン保存と同様に`operation`から`Slice`に設定したSet関数を呼ぶことでstateに保存していますが、使い方として気になることはないでしょうか?

※代行操作中に表示するタブの制限と代行操作トークンの更新処理は別タスクでの実施予定です。

## UIの変更
- [Task2909](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/Task2909?csf=1&web=1&e=TnwgOb)

## 動作確認状況
- ローカルで確認
This commit is contained in:
saito.k 2023-11-06 08:28:40 +00:00
parent 42dab2a45d
commit deb3431d74
57 changed files with 7078 additions and 9185 deletions

File diff suppressed because it is too large Load Diff

View File

@ -38,6 +38,7 @@ export const errorCodes = [
"E010401", // PONumber重複エラー
"E010501", // アカウント不在エラー
"E010502", // アカウント情報変更不可エラー
"E010503", // 代行操作不許可エラー
"E010601", // タスク変更不可エラー(タスクが変更できる状態でない、またはタスクが存在しない)
"E010602", // タスク変更権限不足エラー
"E010603", // タスク不在エラー

View File

@ -1,11 +1,11 @@
import { useEffect } from "react";
import {
isAuthenticatedSelector,
isTokenExpired,
} from "features/auth/selectors";
import { useDispatch, useSelector } from "react-redux";
import { AppDispatch } from "app/store";
import { clearToken } from "features/auth";
import {
clearToken,
isAuthenticatedSelector,
isTokenExpired,
} from "features/auth";
import { useMsal } from "@azure/msal-react";
import { clearUserInfo } from "features/login";

View File

@ -2,8 +2,7 @@ import React, { useCallback } from "react";
import { AppDispatch } from "app/store";
import { decodeToken } from "common/decodeToken";
import { useInterval } from "common/useInterval";
import { updateTokenAsync } from "features/auth/operations";
import { loadAccessToken } from "features/auth/utils";
import { updateTokenAsync, loadAccessToken } from "features/auth";
import { DateTime } from "luxon";
import { useDispatch } from "react-redux";
// アクセストークンを更新する基準の秒数

View File

@ -1,22 +1,30 @@
// 代行操作中に表示するコンポーネント
// ------------------------------------------------------
import React from "react";
import { useSelector } from "react-redux";
import React, { useCallback } from "react";
import { useTranslation } from "react-i18next";
import { getTranslationID } from "translation";
import { useDispatch, useSelector } from "react-redux";
import {
cleanupDelegateAccount,
selectDelegatedCompanyName,
} from "features/partner";
import { clearDelegationToken } from "features/auth";
import { useNavigate } from "react-router-dom";
import styles from "../../styles/app.module.scss";
import exit from "../../assets/images/exit.svg";
import reportWhite from "../../assets/images/report_white.svg";
import { getTranslationID } from "translation";
interface DelegationBarProps {
delegatedCompanyName: string;
}
export const DelegationBar: React.FC<DelegationBarProps> = (
porps
): JSX.Element => {
const { delegatedCompanyName } = porps;
export const DelegationBar: React.FC = (): JSX.Element => {
const delegatedCompanyName = useSelector(selectDelegatedCompanyName);
const { t } = useTranslation();
const dispatch = useDispatch();
const navigate = useNavigate();
const onClickExit = useCallback(() => {
dispatch(clearDelegationToken());
dispatch(cleanupDelegateAccount());
navigate("/partners");
}, [dispatch, navigate]);
return (
<div className={styles.manageInfo}>
@ -25,11 +33,13 @@ export const DelegationBar: React.FC<DelegationBarProps> = (
{t(getTranslationID("common.label.operationInsteadOf"))}
<span>{delegatedCompanyName}</span>
</p>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions */}
<img
src={exit}
className={styles.manageIconClose}
alt="Exit"
title="Exit"
onClick={onClickExit}
/>
</div>
);

View File

@ -11,6 +11,7 @@ import {
selectIsUserNameEmpty,
clearUserInfo,
} from "features/login";
import { useNavigate } from "react-router-dom";
import { getFilteredMenus } from "./utils";
import logo from "../../assets/images/OMS_logo_black.svg";
import ac from "../../assets/images/account_circle.svg";
@ -30,6 +31,7 @@ const LoginedHeader: React.FC<HeaderProps> = (props: HeaderProps) => {
const dispatch: AppDispatch = useDispatch();
const { instance } = useMsal();
const { t } = useTranslation();
const navigate = useNavigate();
// Headerのユーザー情報を取得する
const isUserNameEmpty = useSelector(selectIsUserNameEmpty);
@ -66,8 +68,9 @@ const LoginedHeader: React.FC<HeaderProps> = (props: HeaderProps) => {
<ul>
{filterMenus.map((x) => (
<li key={x.key}>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
href={x.path}
onClick={() => navigate(x.path)}
className={
activePath.toUpperCase() === x.path.toUpperCase()
? styles.isActive

View File

@ -1,8 +1,4 @@
import {
isAdminUser,
isApproveTier,
isStandardUser,
} from "features/auth/utils";
import { isAdminUser, isApproveTier, isStandardUser } from "features/auth";
import { LoginedPaths } from "./types";
import {
ADMIN_ONLY_TABS,

View File

@ -1,11 +1,10 @@
import React, { useCallback, useEffect, useRef } from "react";
import styles from "styles/app.module.scss";
import { SnackbarLevel } from "features/ui/types";
import reportWhite from "../../assets/images/report_white.svg";
import closeWhite from "../../assets/images/close_white.svg";
import checkCircleWhite from "../../assets/images/check_circle_white.svg";
export type SnackbarLevel = "info" | "error";
interface SnackbarProps {
isOpen: boolean;
level: SnackbarLevel;

View File

@ -3,6 +3,7 @@ import type { RootState } from "app/store";
import { ErrorObject, createErrorObject } from "common/errors";
import { getTranslationID } from "translation";
import { openSnackbar } from "features/ui/uiSlice";
import { getAccessToken } from "features/auth";
import {
AccountsApi,
UpdateAccountInfoRequest,
@ -26,7 +27,8 @@ export const getAccountRelationsAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
const usersApi = new UsersApi(config);
@ -72,7 +74,8 @@ export const updateAccountInfoAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountApi = new AccountsApi(config);
@ -125,7 +128,8 @@ export const deleteAccountAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountApi = new AccountsApi(config);

View File

@ -8,14 +8,15 @@ import {
saveRefreshToken,
removeRefreshToken,
} from "./utils";
import { AuthState } from "./state";
import type { AuthState } from "./state";
import { getDelegationTokenAsync, updateTokenAsync } from "./operations";
const initialState: AuthState = {
configuration: initialConfig(),
accessToken: loadAccessToken(),
refreshToken: loadRefreshToken(),
delegatedAccessToken: null,
delegatedRefreshToken: null,
delegationAccessToken: null,
delegationRefreshToken: null,
};
export const authSlice = createSlice({
@ -24,12 +25,10 @@ export const authSlice = createSlice({
reducers: {
setToken: (
state,
action: PayloadAction<
Omit<
AuthState,
"configuration" | "delegatedAccessToken" | "delegatedRefreshToken"
>
>
action: PayloadAction<{
accessToken: string | null;
refreshToken: string | null;
}>
) => {
const { accessToken, refreshToken } = action.payload;
if (accessToken && refreshToken) {
@ -46,9 +45,25 @@ export const authSlice = createSlice({
removeAccessToken();
removeRefreshToken();
},
clearDelegationToken: (state) => {
state.delegationAccessToken = null;
state.delegationRefreshToken = null;
},
},
extraReducers: (builder) => {
builder.addCase(updateTokenAsync.fulfilled, (state, action) => {
const { accessToken } = action.payload;
state.accessToken = accessToken;
saveAccessToken(accessToken);
});
builder.addCase(getDelegationTokenAsync.fulfilled, (state, action) => {
const { accessToken, refreshToken } = action.payload;
state.delegationAccessToken = accessToken;
state.delegationRefreshToken = refreshToken;
});
},
});
export const { setToken, clearToken } = authSlice.actions;
export const { setToken, clearToken, clearDelegationToken } = authSlice.actions;
export default authSlice.reducer;

View File

@ -1 +1,5 @@
export { authSlice, clearToken, setToken } from "./authSlice";
export * from "./authSlice";
export * from "./state";
export * from "./operations";
export * from "./selectors";
export * from "./utils";

View File

@ -1,13 +1,18 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { RootState } from "app/store";
import { AuthApi } from "../../api/api";
import type { RootState } from "app/store";
import { openSnackbar } from "features/ui";
import { getTranslationID } from "translation";
import { ErrorObject, createErrorObject } from "common/errors";
import {
AccessTokenResponse,
AuthApi,
DelegationTokenResponse,
} from "../../api/api";
import { Configuration } from "../../api/configuration";
import { setToken } from "./authSlice";
import { getAccessToken, loadRefreshToken } from "./utils";
export const updateTokenAsync = createAsyncThunk<
{
/* Empty Object */
},
AccessTokenResponse,
void,
{
// rejectした時の返却値の型
@ -19,7 +24,8 @@ export const updateTokenAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, refreshToken } = state.auth;
const { configuration } = state.auth;
const refreshToken = loadRefreshToken();
const config = new Configuration(configuration);
const authApi = new AuthApi(config);
@ -27,16 +33,70 @@ export const updateTokenAsync = createAsyncThunk<
const { data } = await authApi.accessToken({
headers: { authorization: `Bearer ${refreshToken}` },
});
// アクセストークン・リフレッシュトークンをlocalStorageに保存
thunkApi.dispatch(
setToken({
accessToken: data.accessToken,
refreshToken: state.auth.refreshToken,
})
);
return {};
return data;
} catch (e) {
return thunkApi.rejectWithValue({});
}
});
// パートナーのアカウントを代理操作するトークンを取得する
export const getDelegationTokenAsync = createAsyncThunk<
// 正常時の戻り値の型
DelegationTokenResponse,
void,
{
// rejectした時の返却値の型
rejectValue: {
error: ErrorObject;
};
}
>("auth/getDelegationTokenAsync", async (args, thunkApi) => {
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const { delegatedAccountId } = state.partner.apps;
const config = new Configuration(configuration);
const authApi = new AuthApi(config);
try {
if (!delegatedAccountId) {
throw new Error("delegatedAccountId is not set");
}
const { data } = await authApi.delegationToken(
{ delegatedAccountId },
{ headers: { authorization: `Bearer ${accessToken}` } }
);
thunkApi.dispatch(
openSnackbar({
level: "info",
message: getTranslationID("common.message.success"),
})
);
return data;
} catch (e) {
const error = createErrorObject(e);
let errorMessage = getTranslationID("common.message.internalServerError");
if (error.code === "E010503") {
errorMessage = getTranslationID(
"partnerPage.message.delegateNotAllowedError"
);
}
if (error.code === "E010501") {
errorMessage = getTranslationID("partnerPage.message.deleteFailedError");
}
thunkApi.dispatch(
openSnackbar({
level: "error",
message: errorMessage,
})
);
return thunkApi.rejectWithValue({ error });
}
});

View File

@ -1,6 +1,7 @@
import { RootState } from "app/store";
import type { RootState } from "app/store";
import { decodeToken } from "common/decodeToken";
import { DateTime } from "luxon";
import { loadAccessToken } from "./utils";
/**
*
* @param state RootState
@ -24,5 +25,9 @@ export const isTokenExpired = (state: RootState): boolean => {
};
// 代行操作用のトークンを取得する
export const selectDelegatedAccessToken = (state: RootState): string | null =>
state.auth.delegatedAccessToken;
export const selectDelegationAccessToken = (state: RootState): string | null =>
state.auth.delegationAccessToken;
// 代行操作用がなければ通常トークンを取得する
export const selectAccessToken = (state: RootState): string | null =>
state.auth.delegationAccessToken ?? loadAccessToken();

View File

@ -4,6 +4,6 @@ export interface AuthState {
configuration: ConfigurationParameters;
accessToken: string | null;
refreshToken: string | null;
delegatedAccessToken: string | null;
delegatedRefreshToken: string | null;
delegationAccessToken: string | null;
delegationRefreshToken: string | null;
}

View File

@ -1,6 +1,7 @@
import { ConfigurationParameters } from "api";
import { decodeToken } from "../../common/decodeToken";
import { ADMIN_ROLES, USER_ROLES } from "../../components/auth/constants";
import type { AuthState } from "./state";
/**
* Get access token
@ -42,6 +43,22 @@ export const removeRefreshToken = (): void => {
localStorage.removeItem("refreshToken");
};
/**
*
*
* @returns access token
*/
export const getAccessToken = (state: AuthState): string | null =>
state.delegationAccessToken ?? state.accessToken;
/**
*
*
* @returns refresh token
*/
export const getRefreshToken = (state: AuthState): string | null =>
state.delegationRefreshToken ?? state.refreshToken;
// 初期状態のAPI Config
export const initialConfig = (): ConfigurationParameters => {
const config: ConfigurationParameters = {};

View File

@ -2,6 +2,7 @@ import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { getTranslationID } from "translation";
import { openSnackbar } from "features/ui/uiSlice";
import { getAccessToken } from "features/auth";
import {
TasksResponse,
TasksApi,
@ -42,7 +43,8 @@ export const listTasksAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const tasksApi = new TasksApi(config);
@ -89,7 +91,8 @@ export const getSortColumnAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const usersApi = new UsersApi(config);
@ -138,7 +141,8 @@ export const listTypistsAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -174,7 +178,8 @@ export const listTypistGroupsAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -217,7 +222,8 @@ export const updateAssigneeAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const tasksApi = new TasksApi(config);
@ -283,7 +289,8 @@ export const playbackAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const tasksApi = new TasksApi(config);
const usersApi = new UsersApi(config);

View File

@ -1,4 +1,5 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { getAccessToken } from "features/auth";
import { openSnackbar } from "../../ui/uiSlice";
import type { RootState } from "../../../app/store";
import { getTranslationID } from "../../../translation";
@ -26,7 +27,8 @@ export const activateCardLicenseAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const licensesApi = new LicensesApi(config);

View File

@ -1,4 +1,5 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { getAccessToken } from "features/auth";
import { openSnackbar } from "../../ui/uiSlice";
import type { RootState } from "../../../app/store";
import { getTranslationID } from "../../../translation";
@ -24,7 +25,8 @@ export const createCardLicenseAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const licensesApi = new LicensesApi(config);

View File

@ -2,6 +2,7 @@ import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { getTranslationID } from "translation";
import { openSnackbar } from "features/ui/uiSlice";
import { getAccessToken } from "features/auth";
import { LicensesApi } from "../../../api/api";
import { Configuration } from "../../../api/configuration";
import { ErrorObject, createErrorObject } from "../../../common/errors";
@ -27,7 +28,8 @@ export const orderLicenseAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const licensesApi = new LicensesApi(config);

View File

@ -2,6 +2,7 @@ import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { getTranslationID } from "translation";
import { openSnackbar } from "features/ui/uiSlice";
import { getAccessToken } from "features/auth";
import { AccountsApi, LicensesApi } from "../../../api/api";
import { Configuration } from "../../../api/configuration";
import { ErrorObject, createErrorObject } from "../../../common/errors";
@ -26,7 +27,8 @@ export const getLicenseOrderHistoriesAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -95,7 +97,8 @@ export const issueLicenseAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -161,7 +164,8 @@ export const cancelOrderAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const licensesApi = new LicensesApi(config);
@ -223,7 +227,8 @@ export const cancelIssueAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);

View File

@ -2,6 +2,7 @@ import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { getTranslationID } from "translation";
import { openSnackbar } from "features/ui/uiSlice";
import { getAccessToken } from "features/auth";
import {
AccountsApi,
GetLicenseSummaryResponse,
@ -25,7 +26,8 @@ export const getLicenseSummaryAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);

View File

@ -1,4 +1,5 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { getAccessToken } from "features/auth";
import type { RootState } from "../../../app/store";
import { getTranslationID } from "../../../translation";
import { openSnackbar } from "../../ui/uiSlice";
@ -25,7 +26,8 @@ export const getMyAccountAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
try {
@ -67,7 +69,8 @@ export const getPartnerLicenseAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);

View File

@ -1,6 +1,6 @@
import { createSlice } from "@reduxjs/toolkit";
import { getUserInfoAsync } from "./operations";
import { HeaderState } from "./state";
import type { HeaderState } from "./state";
const initialState: HeaderState = {
domain: {

View File

@ -1,5 +1,5 @@
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
import { LoginState } from "./state";
import type { LoginState } from "./state";
import { loginAsync } from "./operations";
const initialState: LoginState = {

View File

@ -1,14 +1,17 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { setToken } from "features/auth/authSlice";
import { AuthApi, UsersApi, GetMyUserResponse } from "../../api/api";
import { setToken, getAccessToken } from "features/auth";
import {
AuthApi,
UsersApi,
GetMyUserResponse,
TokenResponse,
} from "../../api/api";
import { Configuration } from "../../api/configuration";
import { ErrorObject, createErrorObject } from "../../common/errors";
export const loginAsync = createAsyncThunk<
{
//
},
TokenResponse,
{
idToken: string;
},
@ -40,7 +43,7 @@ export const loginAsync = createAsyncThunk<
})
);
return {};
return data;
} catch (e) {
// e ⇒ errorObjectに変換"
const error = createErrorObject(e);
@ -63,7 +66,8 @@ export const getUserInfoAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const usersApi = new UsersApi(config);

View File

@ -1,4 +1,4 @@
import { RootState } from "app/store";
import type { RootState } from "app/store";
export const selectLoginApiCallStatus = (
state: RootState

View File

@ -3,6 +3,7 @@ import type { RootState } from "app/store";
import { ErrorObject, createErrorObject } from "common/errors";
import { getTranslationID } from "translation";
import { openSnackbar } from "features/ui/uiSlice";
import { getAccessToken } from "features/auth";
import {
AccountsApi,
CreatePartnerAccountRequest,
@ -26,7 +27,8 @@ export const createPartnerAccountAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountApi = new AccountsApi(config);
@ -86,7 +88,8 @@ export const getPartnerInfoAsync = createAsyncThunk<
const { limit, offset } = args;
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);

View File

@ -60,6 +60,21 @@ export const partnerSlice = createSlice({
state.apps.limit = limit;
state.apps.offset = offset;
},
changeDelegateAccount: (
state,
action: PayloadAction<{
delegatedAccountId?: number;
delegatedCompanyName?: string;
}>
) => {
const { delegatedAccountId, delegatedCompanyName } = action.payload;
state.apps.delegatedAccountId = delegatedAccountId;
state.apps.delegatedCompanyName = delegatedCompanyName;
},
cleanupDelegateAccount: (state) => {
state.apps.delegatedAccountId = undefined;
state.apps.delegatedCompanyName = undefined;
},
},
extraReducers: (builder) => {
builder.addCase(createPartnerAccountAsync.pending, (state) => {
@ -91,5 +106,7 @@ export const {
changeCountry,
cleanupAddPartner,
savePageInfo,
changeDelegateAccount,
cleanupDelegateAccount,
} = partnerSlice.actions;
export default partnerSlice.reducer;

View File

@ -53,3 +53,9 @@ export const selectCurrentPage = (state: RootState) => {
const page = floor(offset / limit) + 1;
return page;
};
// delegation
export const selectDelegatedAccountId = (state: RootState) =>
state.partner.apps.delegatedAccountId;
export const selectDelegatedCompanyName = (state: RootState) =>
state.partner.apps.delegatedCompanyName;

View File

@ -17,4 +17,6 @@ export interface Apps {
offset: number;
addPartner: CreatePartnerAccountRequest;
isLoading: boolean;
delegatedAccountId?: number;
delegatedCompanyName?: string;
}

View File

@ -5,6 +5,7 @@ import { getTranslationID } from "translation";
import { openSnackbar } from "features/ui/uiSlice";
import { getIdTokenFromLocalStorage } from "common/token";
import { TIERS } from "components/auth/constants";
import { getAccessToken } from "features/auth";
import {
UsersApi,
GetAccountInfoMinimalAccessResponse,
@ -30,7 +31,8 @@ export const getAccountInfoMinimalAccessAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountApi = new AccountsApi(config);
@ -74,7 +76,8 @@ export const getTermsInfoAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const termsApi = new TermsApi(config);
@ -120,7 +123,8 @@ export const updateAcceptedVersionAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const userApi = new UsersApi(config);

View File

@ -2,3 +2,4 @@ export * from "./constants";
export * from "./selectors";
export * from "./state";
export * from "./uiSlice";
export * from "./types";

View File

@ -1,5 +1,5 @@
import { RootState } from "app/store";
import { SnackbarLevel } from "components/snackbar";
import type { RootState } from "app/store";
import { SnackbarLevel } from "./types";
export const selectSnackber = (
state: RootState

View File

@ -1,4 +1,4 @@
import { SnackbarLevel } from "components/snackbar";
import { SnackbarLevel } from "./types";
export interface UIState {
isOpen: boolean;

View File

@ -0,0 +1 @@
export type SnackbarLevel = "info" | "error";

View File

@ -1,5 +1,5 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { SnackbarLevel } from "components/snackbar";
import { SnackbarLevel } from "./types";
import { UIState } from "./state";
import { DEFAULT_SNACKBAR_DURATION } from "./constants";

View File

@ -3,6 +3,7 @@ import type { RootState } from "app/store";
import { USER_ROLES } from "components/auth/constants";
import { openSnackbar } from "features/ui/uiSlice";
import { getTranslationID } from "translation";
import { getAccessToken } from "features/auth";
import {
GetUsersResponse,
UsersApi,
@ -27,7 +28,8 @@ export const listUsersAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const usersApi = new UsersApi(config);
@ -67,7 +69,8 @@ export const addUserAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const usersApi = new UsersApi(config);
const { addUser } = state.user.apps;
@ -134,7 +137,8 @@ export const updateUserAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const usersApi = new UsersApi(config);
const { updateUser } = state.user.apps;
@ -217,7 +221,8 @@ export const getAllocatableLicensesAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const licensesApi = new LicensesApi(config);
@ -266,7 +271,8 @@ export const allocateLicenseAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const usersApi = new UsersApi(config);
@ -335,7 +341,8 @@ export const deallocateLicenseAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const usersApi = new UsersApi(config);

View File

@ -14,6 +14,7 @@ import {
} from "api";
import type { RootState } from "app/store";
import { ErrorObject, createErrorObject } from "common/errors";
import { getAccessToken } from "features/auth";
import { openSnackbar } from "features/ui/uiSlice";
import { getTranslationID } from "translation";
@ -30,7 +31,8 @@ export const listWorkflowAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const workflowsApi = new WorkflowsApi(config);
@ -68,7 +70,8 @@ export const createWorkflowAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const workflowsApi = new WorkflowsApi(config);
const { selectedAssignees, authorId, templateId, worktypeId } =
@ -155,7 +158,8 @@ export const updateWorkflowAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const workflowsApi = new WorkflowsApi(config);
const {
@ -258,7 +262,8 @@ export const getworkflowRelationsAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
const templatesApi = new TemplatesApi(config);
@ -326,7 +331,8 @@ export const deleteWorkflowAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const workflowsApi = new WorkflowsApi(config);

View File

@ -10,6 +10,7 @@ import { ErrorObject, createErrorObject } from "common/errors";
import { openSnackbar } from "features/ui/uiSlice";
import { getTranslationID } from "translation";
import { BlockBlobClient, ContainerClient } from "@azure/storage-blob";
import { getAccessToken } from "features/auth";
export const listTemplateAsync = createAsyncThunk<
GetTemplatesResponse,
@ -24,7 +25,8 @@ export const listTemplateAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const templateApi = new TemplatesApi(config);
@ -62,7 +64,8 @@ export const uploadTemplateAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const { uploadFile } = state.template.apps;
const config = new Configuration(configuration);
const filesApi = new FilesApi(config);

View File

@ -2,6 +2,7 @@ import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { openSnackbar } from "features/ui/uiSlice";
import { getTranslationID } from "translation";
import { getAccessToken } from "features/auth";
import {
AccountsApi,
GetTypistGroupsResponse,
@ -26,7 +27,8 @@ export const listTypistGroupsAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -62,7 +64,8 @@ export const listTypistsAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -100,7 +103,8 @@ export const createTypistGroupAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -152,7 +156,8 @@ export const getTypistGroupAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -204,7 +209,8 @@ export const updateTypistGroupAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const { updateTypistGroupId, selectedTypists, groupName } =
state.typistGroup.apps;
const config = new Configuration(configuration);

View File

@ -2,6 +2,7 @@ import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { openSnackbar } from "features/ui/uiSlice";
import { getTranslationID } from "translation";
import { getAccessToken } from "features/auth";
import {
AccountsApi,
GetOptionItemsResponse,
@ -23,7 +24,8 @@ export const listWorktypesAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
@ -63,7 +65,8 @@ export const addWorktypeAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
// stateからworktypeIdとdescriptionを取得する
@ -130,7 +133,8 @@ export const editWorktypeAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
// stateからselectedId,worktypeId,descriptionを取得する
@ -197,7 +201,8 @@ export const getOptionItemsAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
// stateからselectedIdを取得する
@ -249,7 +254,8 @@ export const editOptionItemsAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
// stateからselectedId,optionItemsを取得する
@ -315,7 +321,8 @@ export const updateActiveWorktypeAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);
const { id } = args;
@ -374,7 +381,8 @@ export const deleteWorktypeAsync = createAsyncThunk<
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { configuration } = state.auth;
const accessToken = getAccessToken(state.auth);
const config = new Configuration(configuration);
const accountsApi = new AccountsApi(config);

View File

@ -21,7 +21,7 @@ import {
import { useTranslation } from "react-i18next";
import { getTranslationID } from "translation";
import { TIERS } from "components/auth/constants";
import { isApproveTier } from "features/auth/utils";
import { isApproveTier } from "features/auth";
import { DeleteAccountPopup } from "./deleteAccountPopup";
import progress_activit from "../../assets/images/progress_activit.svg";

View File

@ -33,7 +33,7 @@ import {
} from "features/dictation";
import { getTranslationID } from "translation";
import { Task } from "api/api";
import { isAdminUser, isAuthorUser, isTypistUser } from "features/auth/utils";
import { isAdminUser, isAuthorUser, isTypistUser } from "features/auth";
import { STATUS, LIMIT_TASK_NUM } from "../../features/dictation";
import uploaded from "../../assets/images/uploaded.svg";
import pending from "../../assets/images/pending.svg";

View File

@ -1,17 +1,17 @@
import React, { useCallback } from "react";
import { loadAccessToken } from "features/auth/utils";
import { decodeToken } from "common/decodeToken";
import { useDispatch } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { AppDispatch } from "app/store";
import { clearToken } from "features/auth";
import { useMsal } from "@azure/msal-react";
import { Token } from "common/token";
import { clearUserInfo } from "features/login";
import { selectAccessToken } from "features/auth/selectors";
import { LicenseSummary } from "./licenseSummary";
import PartnerLicense from "./partnerLicense";
const LicensePage: React.FC = (): JSX.Element => {
const jwt = loadAccessToken();
const jwt = useSelector(selectAccessToken);
const dispatch: AppDispatch = useDispatch();
const { instance } = useMsal();

View File

@ -1,7 +1,7 @@
/* eslint-disable jsx-a11y/control-has-associated-label */
import React, { useCallback, useEffect } from "react";
import { UpdateTokenTimer } from "components/auth/updateTokenTimer";
import { isApproveTier } from "features/auth/utils";
import { isApproveTier } from "features/auth";
import { TIERS } from "components/auth/constants";
import Footer from "components/footer";
import Header from "components/header";
@ -27,7 +27,7 @@ import {
cancelIssueAsync,
} from "features/license/licenseOrderHistory";
import { selectSelectedRow } from "features/license/partnerLicense";
import { selectDelegatedAccessToken } from "features/auth/selectors";
import { selectDelegationAccessToken } from "features/auth/selectors";
import { DelegationBar } from "components/delegate";
import undo from "../../assets/images/undo.svg";
import history from "../../assets/images/history.svg";
@ -49,7 +49,7 @@ export const LicenseOrderHistory: React.FC<LicenseOrderHistoryProps> = (
const isLoading = useSelector(selectIsLoading);
const selectedRow = useSelector(selectSelectedRow);
// 代行操作用のトークンを取得する
const delegatedAccessToken = useSelector(selectDelegatedAccessToken);
const delegationAccessToken = useSelector(selectDelegationAccessToken);
// Return押下時の処理
const returnGui = useCallback(() => {
@ -159,12 +159,11 @@ export const LicenseOrderHistory: React.FC<LicenseOrderHistoryProps> = (
return (
<div
className={`${styles.wrap} ${delegatedAccessToken ? styles.manage : ""}`}
className={`${styles.wrap} ${delegationAccessToken ? styles.manage : ""}`}
>
{
// 代行操作中の場合は、代行操作バーを表示する
// TODO 代行操作中の会社名と、代行操作用のトークンを取得があれば表示するという風にする(別タスクで)
delegatedAccessToken && <DelegationBar delegatedCompanyName="XXXXXX" />
delegationAccessToken && <DelegationBar />
}
<Header />
<UpdateTokenTimer />

View File

@ -12,7 +12,7 @@ import {
selecLicenseSummaryInfo,
} from "features/license/licenseSummary";
import { selectSelectedRow } from "features/license/partnerLicense";
import { selectDelegatedAccessToken } from "features/auth/selectors";
import { selectDelegationAccessToken } from "features/auth/selectors";
import { DelegationBar } from "components/delegate";
import postAdd from "../../assets/images/post_add.svg";
import history from "../../assets/images/history.svg";
@ -36,7 +36,7 @@ export const LicenseSummary: React.FC<LicenseSummaryProps> = (
const [t] = useTranslation();
const selectedRow = useSelector(selectSelectedRow);
// 代行操作用のトークンを取得する
const delegatedAccessToken = useSelector(selectDelegatedAccessToken);
const delegationAccessToken = useSelector(selectDelegationAccessToken);
// popup制御関係
const [islicenseOrderPopupOpen, setIslicenseOrderPopupOpen] = useState(false);
@ -102,16 +102,10 @@ export const LicenseSummary: React.FC<LicenseSummaryProps> = (
{!islicenseOrderHistoryOpen && (
<div
className={`${styles.wrap} ${
delegatedAccessToken ? styles.manage : ""
delegationAccessToken ? styles.manage : ""
}`}
>
{
// 代行操作中の場合は、代行操作バーを表示する
// TODO 代行操作中の会社名と、代行操作用のトークンを取得があれば表示するという風にする(別タスクで)
delegatedAccessToken && (
<DelegationBar delegatedCompanyName="XXXXXX" />
)
}
{delegationAccessToken && <DelegationBar />}
<Header />
<UpdateTokenTimer />

View File

@ -1,7 +1,7 @@
import { useMsal } from "@azure/msal-react";
import { AppDispatch } from "app/store";
import { isIdToken } from "common/token";
import { loadAccessToken, loadRefreshToken } from "features/auth/utils";
import { loadAccessToken, loadRefreshToken } from "features/auth";
import { loginAsync, selectLocalStorageKeyforIdToken } from "features/login";
import React, { useCallback, useEffect } from "react";
import Footer from "components/footer";

View File

@ -6,7 +6,7 @@ import Header from "components/header";
import React, { useCallback, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import styles from "styles/app.module.scss";
import { isApproveTier } from "features/auth/utils";
import { isApproveTier } from "features/auth";
import {
LIMIT_PARTNER_VIEW_NUM,
selectCurrentPage,
@ -17,9 +17,14 @@ import {
getPartnerInfoAsync,
selectPartnersInfo,
} from "features/partner/index";
import { savePageInfo } from "features/partner/partnerSlice";
import {
changeDelegateAccount,
savePageInfo,
} from "features/partner/partnerSlice";
import { getTranslationID } from "translation";
import { useTranslation } from "react-i18next";
import { getDelegationTokenAsync } from "features/auth/operations";
import { useNavigate } from "react-router-dom";
import personAdd from "../../assets/images/person_add.svg";
import { TIERS } from "../../components/auth/constants";
import { AddPartnerAccountPopup } from "./addPartnerAccountPopup";
@ -29,6 +34,7 @@ const PartnerPage: React.FC = (): JSX.Element => {
const dispatch: AppDispatch = useDispatch();
const [isPopupOpen, setIsPopupOpen] = useState(false);
const [t] = useTranslation();
const navigate = useNavigate();
const total = useSelector(selectTotal);
const totalPage = useSelector(selectTotalPage);
const offset = useSelector(selectOffset);
@ -83,6 +89,19 @@ const PartnerPage: React.FC = (): JSX.Element => {
savePageInfo({ limit: LIMIT_PARTNER_VIEW_NUM, offset: targetOffset })
);
};
// 代理操作開始処理
const startDealerManagement = useCallback(
async (delegatedAccountId: number, delegatedCompanyName: string) => {
dispatch(
changeDelegateAccount({ delegatedAccountId, delegatedCompanyName })
);
const { meta } = await dispatch(getDelegationTokenAsync());
if (meta.requestStatus === "fulfilled") {
navigate("/user");
}
},
[dispatch, navigate]
);
// HTML
return (
@ -160,8 +179,8 @@ const PartnerPage: React.FC = (): JSX.Element => {
<tr>
<td className={styles.clm0}>
<ul className={styles.menuInTable}>
<li>
{isVisibleButton && (
{isVisibleButton && (
<li>
<a>
{t(
getTranslationID(
@ -169,8 +188,27 @@ const PartnerPage: React.FC = (): JSX.Element => {
)
)}
</a>
)}
</li>
</li>
)}
{isVisibleDealerManagement && (
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
<a
onClick={() => {
startDealerManagement(x.accountId, x.name);
}}
className={
x.dealerManagement ? "" : styles.isDisable
}
>
{t(
getTranslationID(
"partnerPage.label.dealerManagement"
)
)}
</a>
</li>
)}
</ul>
</td>
<td>{x.name}</td>

View File

@ -14,15 +14,17 @@ import {
listTemplateAsync,
selectIsLoading,
} from "features/workflow/template";
import { selectDelegationAccessToken } from "features/auth/selectors";
import { DelegationBar } from "components/delegate";
import { selectDelegatedAccessToken } from "features/auth/selectors";
import { useNavigate } from "react-router-dom";
import { AddTemplateFilePopup } from "./addTemplateFilePopup";
export const TemplateFilePage: React.FC = () => {
const dispatch: AppDispatch = useDispatch();
const [t] = useTranslation();
// authStateに配置予定の代行操作用のトークンを取得する
const delegatedAccessToken = useSelector(selectDelegatedAccessToken);
const navigate = useNavigate();
// 代行操作用のトークンを取得する
const delegationAccessToken = useSelector(selectDelegationAccessToken);
const templates = useSelector(selectTemplates);
const isLoading = useSelector(selectIsLoading);
@ -44,16 +46,10 @@ export const TemplateFilePage: React.FC = () => {
)}
<div
className={`${styles.wrap} ${
delegatedAccessToken ? styles.manage : ""
delegationAccessToken ? styles.manage : ""
}`}
>
{
// 代行操作中の場合は、代行操作バーを表示する
// TODO 代行操作中の会社名と、代行操作用のトークンを取得があれば表示するという風にする(別タスクで)
delegatedAccessToken && (
<DelegationBar delegatedCompanyName="XXXXXX" />
)
}
{delegationAccessToken && <DelegationBar />}
<Header />
<UpdateTokenTimer />
<main className={styles.main}>
@ -71,8 +67,9 @@ export const TemplateFilePage: React.FC = () => {
<div>
<ul className={`${styles.menuAction} ${styles.worktype}`}>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
href="/workflow"
onClick={() => navigate("/workflow")}
className={`${styles.menuLink} ${styles.isActive}`}
>
<img src={undo} alt="" className={styles.menuIcon} />

View File

@ -15,16 +15,18 @@ import {
import { AppDispatch } from "app/store";
import { useTranslation } from "react-i18next";
import { getTranslationID } from "translation";
import { selectDelegatedAccessToken } from "features/auth/selectors";
import { selectDelegationAccessToken } from "features/auth/selectors";
import { DelegationBar } from "components/delegate";
import { EditTypistGroupPopup } from "./editTypistGroupPopup";
import { useNavigate } from "react-router-dom";
import { AddTypistGroupPopup } from "./addTypistGroupPopup";
import { EditTypistGroupPopup } from "./editTypistGroupPopup";
const TypistGroupSettingPage: React.FC = (): JSX.Element => {
const dispatch: AppDispatch = useDispatch();
const [t] = useTranslation();
const navigate = useNavigate();
// 代行操作用のトークンを取得する
const delegatedAccessToken = useSelector(selectDelegatedAccessToken);
const delegationAccessToken = useSelector(selectDelegationAccessToken);
const isLoading = useSelector(selectIsLoading);
const typistGroup = useSelector(selectTypistGroups);
@ -66,16 +68,10 @@ const TypistGroupSettingPage: React.FC = (): JSX.Element => {
/>
<div
className={`${styles.wrap} ${
delegatedAccessToken ? styles.manage : ""
delegationAccessToken ? styles.manage : ""
}`}
>
{
// 代行操作中の場合は、代行操作バーを表示する
// TODO 代行操作中の会社名と、代行操作用のトークンを取得があれば表示するという風にする(別タスクで)
delegatedAccessToken && (
<DelegationBar delegatedCompanyName="XXXXXX" />
)
}
{delegationAccessToken && <DelegationBar />}
<Header />
<UpdateTokenTimer />
<main className={styles.main}>
@ -93,8 +89,9 @@ const TypistGroupSettingPage: React.FC = (): JSX.Element => {
<div>
<ul className={styles.menuAction}>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
href="/workflow"
onClick={() => navigate("/workflow")}
className={`${styles.menuLink} ${styles.isActive}`}
>
<img src={undo} alt="" className={styles.menuIcon} />

View File

@ -15,14 +15,14 @@ import { useTranslation } from "react-i18next";
import { getTranslationID } from "translation";
import { isLicenseStatusType, UserView } from "features/user/types";
import { LICENSE_STATUS } from "features/user/constants";
import { isApproveTier } from "features/auth/utils";
import { isApproveTier } from "features/auth";
import { TIERS } from "components/auth/constants";
import {
changeUpdateUser,
changeLicenseAllocateUser,
} from "features/user/userSlice";
import { DelegationBar } from "components/delegate";
import { selectDelegatedAccessToken } from "features/auth/selectors";
import { selectDelegationAccessToken } from "features/auth/selectors";
import personAdd from "../../assets/images/person_add.svg";
import checkFill from "../../assets/images/check_fill.svg";
import checkOutline from "../../assets/images/check_outline.svg";
@ -35,7 +35,7 @@ const UserListPage: React.FC = (): JSX.Element => {
const dispatch: AppDispatch = useDispatch();
const [t] = useTranslation();
// 代行操作用のトークンを取得する
const delegatedAccessToken = useSelector(selectDelegatedAccessToken);
const delegationAccessToken = useSelector(selectDelegationAccessToken);
const [isPopupOpen, setIsPopupOpen] = useState(false);
const [isUpdatePopupOpen, setIsUpdatePopupOpen] = useState(false);
@ -112,16 +112,10 @@ const UserListPage: React.FC = (): JSX.Element => {
/>
<div
className={`${styles.wrap} ${
delegatedAccessToken ? styles.manage : ""
delegationAccessToken ? styles.manage : ""
}`}
>
{
// 代行操作中の場合は、代行操作バーを表示する
// TODO 代行操作中の会社名と、代行操作用のトークンを取得があれば表示するという風にする(別タスクで)
delegatedAccessToken && (
<DelegationBar delegatedCompanyName="XXXXXX" />
)
}
{delegationAccessToken && <DelegationBar />}
<Header />
<UpdateTokenTimer />
<main className={styles.main}>

View File

@ -21,8 +21,9 @@ import {
deleteWorktypeAsync,
} from "features/workflow/worktype";
import { AppDispatch } from "app/store";
import { selectDelegatedAccessToken } from "features/auth/selectors";
import { selectDelegationAccessToken } from "features/auth/selectors";
import { DelegationBar } from "components/delegate";
import { useNavigate } from "react-router-dom";
import { AddWorktypeIdPopup } from "./addWorktypeIdPopup";
import { EditWorktypeIdPopup } from "./editWorktypeIdPopup";
import { EditOptionItemsPopup } from "./editOptionItemsPopup";
@ -30,8 +31,9 @@ import { EditOptionItemsPopup } from "./editOptionItemsPopup";
const WorktypeIdSettingPage: React.FC = (): JSX.Element => {
const dispatch: AppDispatch = useDispatch();
const [t] = useTranslation();
const navigate = useNavigate();
// 代行操作用のトークンを取得する
const delegatedAccessToken = useSelector(selectDelegatedAccessToken);
const delegationAccessToken = useSelector(selectDelegationAccessToken);
const isLoading = useSelector(selectIsLoading);
const worktypes = useSelector(selectWorktypes);
const activeWorktypeId = useSelector(selectActiveWorktypeId);
@ -131,16 +133,10 @@ const WorktypeIdSettingPage: React.FC = (): JSX.Element => {
/>
<div
className={`${styles.wrap} ${
delegatedAccessToken ? styles.manage : ""
delegationAccessToken ? styles.manage : ""
}`}
>
{
// 代行操作中の場合は、代行操作バーを表示する
// TODO 代行操作中の会社名と、代行操作用のトークンを取得があれば表示するという風にする(別タスクで)
delegatedAccessToken && (
<DelegationBar delegatedCompanyName="XXXXXX" />
)
}
{delegationAccessToken && <DelegationBar />}
<Header />
<UpdateTokenTimer />
<main className={styles.main}>
@ -157,8 +153,9 @@ const WorktypeIdSettingPage: React.FC = (): JSX.Element => {
<div>
<ul className={`${styles.menuAction} ${styles.worktype}`}>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
href="/workflow"
onClick={() => navigate("/workflow")}
className={`${styles.menuLink} ${styles.isActive}`}
>
<img src={undo} alt="" className={styles.menuIcon} />

View File

@ -17,23 +17,25 @@ import {
selectIsLoading,
selectWorkflows,
} from "features/workflow";
import { DelegationBar } from "components/delegate";
import { selectDelegationAccessToken } from "features/auth/selectors";
import progress_activit from "assets/images/progress_activit.svg";
import { getTranslationID } from "translation";
import { DelegationBar } from "components/delegate";
import { selectDelegatedAccessToken } from "features/auth/selectors";
import { useNavigate } from "react-router-dom";
import { EditWorkflowPopup } from "./editworkflowPopup";
import { AddWorkflowPopup } from "./addworkflowPopup";
const WorkflowPage: React.FC = (): JSX.Element => {
const dispatch: AppDispatch = useDispatch();
const [t] = useTranslation();
const navigate = useNavigate();
// 追加Popupの表示制御
const [isShowAddPopup, setIsShowAddPopup] = useState<boolean>(false);
// 編集Popupの表示制御
const [isShowEditPopup, setIsShowEditPopup] = useState<boolean>(false);
// 代行操作用のトークンを取得する
const delegatedAccessToken = useSelector(selectDelegatedAccessToken);
const delegationAccessToken = useSelector(selectDelegationAccessToken);
const workflows = useSelector(selectWorkflows);
const isLoading = useSelector(selectIsLoading);
@ -76,16 +78,10 @@ const WorkflowPage: React.FC = (): JSX.Element => {
)}
<div
className={`${styles.wrap} ${
delegatedAccessToken ? styles.manage : ""
delegationAccessToken ? styles.manage : ""
}`}
>
{
// 代行操作中の場合は、代行操作バーを表示する
// TODO 代行操作中の会社名と、代行操作用のトークンを取得があれば表示するという風にする(別タスクで)
delegatedAccessToken && (
<DelegationBar delegatedCompanyName="XXXXXX" />
)
}
{delegationAccessToken && <DelegationBar />}
<Header />
<UpdateTokenTimer />
<main className={styles.main}>
@ -115,8 +111,9 @@ const WorkflowPage: React.FC = (): JSX.Element => {
</a>
</li>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
href="/workflow/template"
onClick={() => navigate("/workflow/template")}
className={`${styles.menuLink} ${styles.isActive}`}
>
<img
@ -130,8 +127,9 @@ const WorkflowPage: React.FC = (): JSX.Element => {
</a>
</li>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
href="/workflow/worktype-id"
onClick={() => navigate("/workflow/worktype-id")}
className={`${styles.menuLink} ${styles.isActive}`}
>
<img
@ -145,8 +143,9 @@ const WorkflowPage: React.FC = (): JSX.Element => {
</a>
</li>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
href="/workflow/typist-group"
onClick={() => navigate("/workflow/typist-group")}
className={`${styles.menuLink} ${styles.isActive}`}
>
<img

View File

@ -470,6 +470,11 @@
"dealerManagement": "Erlauben Sie dem Händler, Änderungen vorzunehmen",
"partners": "Partner",
"deleteAccount": "Konto löschen"
},
"message": {
"delegateNotAllowedError": "(de)パートナーの代行操作が許可されていません。画面を更新し、再度ご確認ください。",
"deleteFailedError": "(de)代行操作に失敗しました。画面を更新し、再度ご確認ください。",
"delegateCancelError": "(de)代行操作の許可が取り消されたため、代行操作を終了しました。"
}
},
"accountPage": {

View File

@ -470,6 +470,11 @@
"dealerManagement": "Dealer Management",
"partners": "Partners",
"deleteAccount": "Delete Account"
},
"message": {
"delegateNotAllowedError": "パートナーの代行操作が許可されていません。画面を更新し、再度ご確認ください。",
"deleteFailedError": "代行操作に失敗しました。画面を更新し、再度ご確認ください。",
"delegateCancelError": "代行操作の許可が取り消されたため、代行操作を終了しました。"
}
},
"accountPage": {

View File

@ -470,6 +470,11 @@
"dealerManagement": "Permitir que el distribuidor realice los cambios",
"partners": "Socios",
"deleteAccount": "Borrar cuenta"
},
"message": {
"delegateNotAllowedError": "(es)パートナーの代行操作が許可されていません。画面を更新し、再度ご確認ください。",
"deleteFailedError": "(es)代行操作に失敗しました。画面を更新し、再度ご確認ください。",
"delegateCancelError": "(es)代行操作の許可が取り消されたため、代行操作を終了しました。"
}
},
"accountPage": {

View File

@ -470,6 +470,11 @@
"dealerManagement": "Autoriser le revendeur à modifier les paramètres",
"partners": "Partenaires",
"deleteAccount": "Supprimer le compte"
},
"message": {
"delegateNotAllowedError": "(fr)パートナーの代行操作が許可されていません。画面を更新し、再度ご確認ください。",
"deleteFailedError": "(fr)代行操作に失敗しました。画面を更新し、再度ご確認ください。",
"delegateCancelError": "(fr)代行操作の許可が取り消されたため、代行操作を終了しました。"
}
},
"accountPage": {