diff --git a/db/init/init_accounts_auto_increment.sql b/db/init/init_accounts_auto_increment.sql new file mode 100644 index 0000000..7d0aa25 --- /dev/null +++ b/db/init/init_accounts_auto_increment.sql @@ -0,0 +1,4 @@ +-- [OMDS_IS-231] アカウントIDの開始番号調整 | 課題の表示 | Backlog 対応 +-- IDからアカウント数が推測されるため、ユーザ指定の任意値を最初の番号とする +-- 一度しか実行しないため、migrate fileではなくDBの初期値として扱う。移行時の実行を想定 +ALTER TABLE accounts AUTO_INCREMENT = 853211; diff --git a/dictation_client/src/common/errors/code.ts b/dictation_client/src/common/errors/code.ts index da0d9b9..50d91db 100644 --- a/dictation_client/src/common/errors/code.ts +++ b/dictation_client/src/common/errors/code.ts @@ -54,6 +54,8 @@ export const errorCodes = [ "E010809", // ライセンス発行キャンセル不可エラー(ステータスが変えられている場合) "E010810", // ライセンス発行キャンセル不可エラー(発行から一定期間経過した場合) "E010811", // ライセンス発行キャンセル不可エラー(発行したライセンスが割り当てされている場合) + "E010908", // タイピストグループ不在エラー + "E010909", // タイピストグループ名重複エラー "E011001", // ワークタイプ重複エラー "E011002", // ワークタイプ登録上限超過エラー "E011003", // ワークタイプ不在エラー diff --git a/dictation_client/src/components/auth/constants.ts b/dictation_client/src/components/auth/constants.ts index ef98041..dd8ec52 100644 --- a/dictation_client/src/components/auth/constants.ts +++ b/dictation_client/src/components/auth/constants.ts @@ -43,7 +43,12 @@ export const UNAUTHORIZED_TO_CONTINUE_ERROR_CODES = [ * ローカルストレージに残すキー類 * @const {string[]} */ -export const KEYS_TO_PRESERVE = ["accessToken", "refreshToken", "displayInfo"]; +export const KEYS_TO_PRESERVE = [ + "accessToken", + "refreshToken", + "displayInfo", + "sortCriteria", +]; /** * アクセストークンを更新する基準の秒数 diff --git a/dictation_client/src/features/dictation/constants.ts b/dictation_client/src/features/dictation/constants.ts index 69eae60..f7e22d1 100644 --- a/dictation_client/src/features/dictation/constants.ts +++ b/dictation_client/src/features/dictation/constants.ts @@ -28,6 +28,13 @@ export const SORTABLE_COLUMN = { export type SortableColumnType = typeof SORTABLE_COLUMN[keyof typeof SORTABLE_COLUMN]; +export const isSortableColumnType = ( + value: string +): value is SortableColumnType => { + const arg = value as SortableColumnType; + return Object.values(SORTABLE_COLUMN).includes(arg); +}; + export type SortableColumnList = typeof SORTABLE_COLUMN[keyof typeof SORTABLE_COLUMN]; @@ -38,6 +45,10 @@ export const DIRECTION = { export type DirectionType = typeof DIRECTION[keyof typeof DIRECTION]; +// DirectionTypeの型チェック関数 +export const isDirectionType = (arg: string): arg is DirectionType => + arg in DIRECTION; + export interface DisplayInfoType { JobNumber: boolean; Status: boolean; diff --git a/dictation_client/src/features/dictation/operations.ts b/dictation_client/src/features/dictation/operations.ts index 23b98f4..99b426e 100644 --- a/dictation_client/src/features/dictation/operations.ts +++ b/dictation_client/src/features/dictation/operations.ts @@ -280,7 +280,6 @@ export const playbackAsync = createAsyncThunk< direction: DirectionType; paramName: SortableColumnType; audioFileId: number; - isTypist: boolean; }, { // rejectした時の返却値の型 @@ -289,7 +288,7 @@ export const playbackAsync = createAsyncThunk< }; } >("dictations/playbackAsync", async (args, thunkApi) => { - const { audioFileId, direction, paramName, isTypist } = args; + const { audioFileId, direction, paramName } = args; // apiのConfigurationを取得する const { getState } = thunkApi; @@ -300,15 +299,12 @@ export const playbackAsync = createAsyncThunk< const tasksApi = new TasksApi(config); const usersApi = new UsersApi(config); try { - // ユーザーがタイピストである場合に、ソート条件を保存する - if (isTypist) { - await usersApi.updateSortCriteria( - { direction, paramName }, - { - headers: { authorization: `Bearer ${accessToken}` }, - } - ); - } + await usersApi.updateSortCriteria( + { direction, paramName }, + { + headers: { authorization: `Bearer ${accessToken}` }, + } + ); await tasksApi.checkout(audioFileId, { headers: { authorization: `Bearer ${accessToken}` }, }); diff --git a/dictation_client/src/features/license/licenseOrder/operations.ts b/dictation_client/src/features/license/licenseOrder/operations.ts index aaf3a84..abd5981 100644 --- a/dictation_client/src/features/license/licenseOrder/operations.ts +++ b/dictation_client/src/features/license/licenseOrder/operations.ts @@ -62,6 +62,12 @@ export const orderLicenseAsync = createAsyncThunk< ); } + if (error.code === "E010501") { + errorMessage = getTranslationID( + "licenseOrderPage.message.dealerNotFoundError" + ); + } + thunkApi.dispatch( openSnackbar({ level: "error", diff --git a/dictation_client/src/features/workflow/typistGroup/operations.ts b/dictation_client/src/features/workflow/typistGroup/operations.ts index a98ff1b..70594c6 100644 --- a/dictation_client/src/features/workflow/typistGroup/operations.ts +++ b/dictation_client/src/features/workflow/typistGroup/operations.ts @@ -122,11 +122,17 @@ export const createTypistGroupAsync = createAsyncThunk< } catch (e) { // e ⇒ errorObjectに変換" const error = createErrorObject(e); - - const message = - error.statusCode === 400 - ? getTranslationID("typistGroupSetting.message.groupSaveFailedError") - : getTranslationID("common.message.internalServerError"); + let message = getTranslationID("common.message.internalServerError"); + if (error.code === "E010204") { + message = getTranslationID( + "typistGroupSetting.message.groupSaveFailedError" + ); + } + if (error.code === "E010909") { + message = getTranslationID( + "typistGroupSetting.message.GroupNameAlreadyExistError" + ); + } thunkApi.dispatch( openSnackbar({ @@ -242,10 +248,17 @@ export const updateTypistGroupAsync = createAsyncThunk< // e ⇒ errorObjectに変換" const error = createErrorObject(e); - const message = - error.statusCode === 400 - ? getTranslationID("typistGroupSetting.message.groupSaveFailedError") - : getTranslationID("common.message.internalServerError"); + let message = getTranslationID("common.message.internalServerError"); + if (error.code === "E010204" || error.code === "E010908") { + message = getTranslationID( + "typistGroupSetting.message.groupSaveFailedError" + ); + } + if (error.code === "E010909") { + message = getTranslationID( + "typistGroupSetting.message.GroupNameAlreadyExistError" + ); + } thunkApi.dispatch( openSnackbar({ diff --git a/dictation_client/src/pages/AccountPage/index.tsx b/dictation_client/src/pages/AccountPage/index.tsx index 6fd0877..8e3a149 100644 --- a/dictation_client/src/pages/AccountPage/index.tsx +++ b/dictation_client/src/pages/AccountPage/index.tsx @@ -191,23 +191,32 @@ const AccountPage: React.FC = (): JSX.Element => { )} {isTier5 && ( -
- {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */} - -
+ <> +
+ {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */} + +
+
+ {t( + getTranslationID( + "accountPage.text.dealerManagementAnnotation" + ) + )} +
+ )} {!isTier5 &&
-
} @@ -374,6 +383,15 @@ const AccountPage: React.FC = (): JSX.Element => { className={styles.icLoading} alt="Loading" /> + {isTier5 && ( +

+ {t( + getTranslationID( + "accountPage.text.dealerManagementAnnotation" + ) + )} +

+ )} {isTier5 && ( diff --git a/dictation_client/src/pages/DictationPage/index.tsx b/dictation_client/src/pages/DictationPage/index.tsx index bb70c47..1d3abcc 100644 --- a/dictation_client/src/pages/DictationPage/index.tsx +++ b/dictation_client/src/pages/DictationPage/index.tsx @@ -33,6 +33,8 @@ import { playbackAsync, cancelAsync, PRIORITY, + isSortableColumnType, + isDirectionType, } from "features/dictation"; import { getTranslationID } from "translation"; import { Task } from "api/api"; @@ -242,6 +244,12 @@ const DictationPage: React.FC = (): JSX.Element => { dispatch(changeDirection({ direction: currentDirection })); dispatch(changeParamName({ paramName })); + // ローカルストレージにソート情報を保存する + localStorage.setItem( + "sortCriteria", + `direction:${currentDirection},paramName:${paramName}` + ); + const filter = getFilter( filterUploaded, filterInProgress, @@ -348,10 +356,11 @@ const DictationPage: React.FC = (): JSX.Element => { audioFileId, direction: sortDirection, paramName: sortableParamName, - isTypist, }) ); if (meta.requestStatus === "fulfilled") { + // ローカルストレージにソート情報を削除する + localStorage.removeItem("sortCriteria"); const filter = getFilter( filterUploaded, filterInProgress, @@ -388,7 +397,6 @@ const DictationPage: React.FC = (): JSX.Element => { filterInProgress, filterPending, filterUploaded, - isTypist, sortDirection, sortableParamName, t, @@ -522,13 +530,39 @@ const DictationPage: React.FC = (): JSX.Element => { dispatch(changeDisplayInfo({ column: displayInfo })); const filter = getFilter(true, true, true, true, false); + const { meta, payload } = await dispatch(getSortColumnAsync()); if ( meta.requestStatus === "fulfilled" && payload && !("error" in payload) ) { - const { direction, paramName } = payload; + // ソート情報をローカルストレージから取得する + const sortColumnValue = localStorage.getItem("sortCriteria") ?? ""; + let direction: DirectionType; + let paramName: SortableColumnType; + if (sortColumnValue === "") { + direction = payload.direction; + paramName = payload.paramName; + } else { + // ソート情報をDirectionとParamNameに分割する + const sortColumn = sortColumnValue?.split(","); + const localStorageDirection = sortColumn[0].split(":")[1] ?? ""; + + const localStorageParamName = sortColumn[1]?.split(":")[1] ?? ""; + + // 正常なソート情報がローカルストレージに存在する場合はローカルストレージの情報を使用する + direction = isDirectionType(localStorageDirection) + ? localStorageDirection + : payload.direction; + paramName = isSortableColumnType(localStorageParamName) + ? localStorageParamName + : payload.paramName; + + dispatch(changeDirection({ direction })); + dispatch(changeParamName({ paramName })); + } + dispatch( listTasksAsync({ limit: LIMIT_TASK_NUM, @@ -1082,7 +1116,13 @@ const DictationPage: React.FC = (): JSX.Element => { {(isChangeTranscriptionistPopupOpen || !isLoading) && tasks.length !== 0 && tasks.map((x) => ( - +