Merge branch 'develop'

This commit is contained in:
saito.k 2023-09-29 15:33:34 +09:00
commit 0bcc518db1
68 changed files with 4837 additions and 1730 deletions

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,7 @@
"dependencies": {
"@azure/msal-browser": "^2.33.0",
"@azure/msal-react": "^1.5.3",
"@azure/storage-blob": "^12.16.0",
"@reduxjs/toolkit": "^1.8.3",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
@ -51,6 +52,7 @@
},
"devDependencies": {
"@babel/core": "^7.18.6",
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
"@mdx-js/react": "^2.1.2",
"@openapitools/openapi-generator-cli": "^2.5.2",
"@types/lodash": "^4.14.191",

View File

@ -20,6 +20,7 @@ import WorkflowPage from "pages/WorkflowPage";
import TypistGroupSettingPage from "pages/TypistGroupSettingPage";
import WorktypeIdSettingPage from "pages/WorkTypeIdSettingPage";
import AccountPage from "pages/AccountPage";
import { TemplateFilePage } from "pages/TemplateFilePage";
const AppRouter: React.FC = () => (
<Routes>
@ -72,6 +73,10 @@ const AppRouter: React.FC = () => (
path="/workflow/worktype-id"
element={<RouteAuthGuard component={<WorktypeIdSettingPage />} />}
/>
<Route
path="/workflow/template"
element={<RouteAuthGuard component={<TemplateFilePage />} />}
/>
<Route
path="/partners"
element={<RouteAuthGuard component={<PartnerPage />} />}

View File

@ -561,6 +561,19 @@ export interface DeallocateLicenseRequest {
*/
'userId': number;
}
/**
*
* @export
* @interface DeleteAccountRequest
*/
export interface DeleteAccountRequest {
/**
* ID
* @type {number}
* @memberof DeleteAccountRequest
*/
'accountId': number;
}
/**
*
* @export
@ -905,6 +918,19 @@ export interface GetSortCriteriaResponse {
*/
'paramName': string;
}
/**
*
* @export
* @interface GetTemplatesResponse
*/
export interface GetTemplatesResponse {
/**
*
* @type {Array<TemplateFile>}
* @memberof GetTemplatesResponse
*/
'templates': Array<TemplateFile>;
}
/**
*
* @export
@ -1631,6 +1657,57 @@ export interface TemplateDownloadLocationResponse {
*/
'url': string;
}
/**
*
* @export
* @interface TemplateFile
*/
export interface TemplateFile {
/**
* ID
* @type {number}
* @memberof TemplateFile
*/
'id': number;
/**
*
* @type {string}
* @memberof TemplateFile
*/
'name': string;
}
/**
*
* @export
* @interface TemplateUploadFinishedRequest
*/
export interface TemplateUploadFinishedRequest {
/**
*
* @type {string}
* @memberof TemplateUploadFinishedRequest
*/
'name': string;
/**
* URL
* @type {string}
* @memberof TemplateUploadFinishedRequest
*/
'url': string;
}
/**
*
* @export
* @interface TemplateUploadLocationResponse
*/
export interface TemplateUploadLocationResponse {
/**
*
* @type {string}
* @memberof TemplateUploadLocationResponse
*/
'url': string;
}
/**
*
* @export
@ -2154,6 +2231,46 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat
options: localVarRequestOptions,
};
},
/**
*
* @summary
* @param {DeleteAccountRequest} deleteAccountRequest
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
deleteAccount: async (deleteAccountRequest: DeleteAccountRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'deleteAccountRequest' is not null or undefined
assertParamExists('deleteAccount', 'deleteAccountRequest', deleteAccountRequest)
const localVarPath = `/accounts/delete`;
// 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(deleteAccountRequest, localVarRequestOptions, configuration)
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @summary
@ -2611,9 +2728,9 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
me: async (updateAccountInfoRequest: UpdateAccountInfoRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
updateAccountInfo: async (updateAccountInfoRequest: UpdateAccountInfoRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'updateAccountInfoRequest' is not null or undefined
assertParamExists('me', 'updateAccountInfoRequest', updateAccountInfoRequest)
assertParamExists('updateAccountInfo', 'updateAccountInfoRequest', updateAccountInfoRequest)
const localVarPath = `/accounts/me`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
@ -2852,6 +2969,17 @@ export const AccountsApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.createWorktype(createWorktypesRequest, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
*
* @summary
* @param {DeleteAccountRequest} deleteAccountRequest
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async deleteAccount(deleteAccountRequest: DeleteAccountRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.deleteAccount(deleteAccountRequest, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
*
* @summary
@ -2987,8 +3115,8 @@ export const AccountsApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async me(updateAccountInfoRequest: UpdateAccountInfoRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.me(updateAccountInfoRequest, options);
async updateAccountInfo(updateAccountInfoRequest: UpdateAccountInfoRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.updateAccountInfo(updateAccountInfoRequest, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
@ -3097,6 +3225,16 @@ export const AccountsApiFactory = function (configuration?: Configuration, baseP
createWorktype(createWorktypesRequest: CreateWorktypesRequest, options?: any): AxiosPromise<object> {
return localVarFp.createWorktype(createWorktypesRequest, options).then((request) => request(axios, basePath));
},
/**
*
* @summary
* @param {DeleteAccountRequest} deleteAccountRequest
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
deleteAccount(deleteAccountRequest: DeleteAccountRequest, options?: any): AxiosPromise<object> {
return localVarFp.deleteAccount(deleteAccountRequest, options).then((request) => request(axios, basePath));
},
/**
*
* @summary
@ -3220,8 +3358,8 @@ export const AccountsApiFactory = function (configuration?: Configuration, baseP
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
me(updateAccountInfoRequest: UpdateAccountInfoRequest, options?: any): AxiosPromise<object> {
return localVarFp.me(updateAccountInfoRequest, options).then((request) => request(axios, basePath));
updateAccountInfo(updateAccountInfoRequest: UpdateAccountInfoRequest, options?: any): AxiosPromise<object> {
return localVarFp.updateAccountInfo(updateAccountInfoRequest, options).then((request) => request(axios, basePath));
},
/**
*
@ -3338,6 +3476,18 @@ export class AccountsApi extends BaseAPI {
return AccountsApiFp(this.configuration).createWorktype(createWorktypesRequest, options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary
* @param {DeleteAccountRequest} deleteAccountRequest
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof AccountsApi
*/
public deleteAccount(deleteAccountRequest: DeleteAccountRequest, options?: AxiosRequestConfig) {
return AccountsApiFp(this.configuration).deleteAccount(deleteAccountRequest, options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary
@ -3486,8 +3636,8 @@ export class AccountsApi extends BaseAPI {
* @throws {RequiredError}
* @memberof AccountsApi
*/
public me(updateAccountInfoRequest: UpdateAccountInfoRequest, options?: AxiosRequestConfig) {
return AccountsApiFp(this.configuration).me(updateAccountInfoRequest, options).then((request) => request(this.axios, this.basePath));
public updateAccountInfo(updateAccountInfoRequest: UpdateAccountInfoRequest, options?: AxiosRequestConfig) {
return AccountsApiFp(this.configuration).updateAccountInfo(updateAccountInfoRequest, options).then((request) => request(this.axios, this.basePath));
}
/**
@ -3956,6 +4106,80 @@ export const FilesApiAxiosParamCreator = function (configuration?: Configuration
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @summary
* @param {TemplateUploadFinishedRequest} templateUploadFinishedRequest
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
uploadTemplateFinished: async (templateUploadFinishedRequest: TemplateUploadFinishedRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'templateUploadFinishedRequest' is not null or undefined
assertParamExists('uploadTemplateFinished', 'templateUploadFinishedRequest', templateUploadFinishedRequest)
const localVarPath = `/files/template/upload-finished`;
// 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(templateUploadFinishedRequest, localVarRequestOptions, configuration)
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
* Blob Storage上のテンプレートファイルのアップロード先アクセスURLを取得します
* @summary
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
uploadTemplateLocation: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/files/template/upload-location`;
// 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: 'GET', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication bearer required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration)
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
@ -4018,6 +4242,27 @@ export const FilesApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.uploadLocation(options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
*
* @summary
* @param {TemplateUploadFinishedRequest} templateUploadFinishedRequest
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async uploadTemplateFinished(templateUploadFinishedRequest: TemplateUploadFinishedRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.uploadTemplateFinished(templateUploadFinishedRequest, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
* Blob Storage上のテンプレートファイルのアップロード先アクセスURLを取得します
* @summary
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async uploadTemplateLocation(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<TemplateUploadLocationResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.uploadTemplateLocation(options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
}
};
@ -4067,6 +4312,25 @@ export const FilesApiFactory = function (configuration?: Configuration, basePath
uploadLocation(options?: any): AxiosPromise<AudioUploadLocationResponse> {
return localVarFp.uploadLocation(options).then((request) => request(axios, basePath));
},
/**
*
* @summary
* @param {TemplateUploadFinishedRequest} templateUploadFinishedRequest
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
uploadTemplateFinished(templateUploadFinishedRequest: TemplateUploadFinishedRequest, options?: any): AxiosPromise<object> {
return localVarFp.uploadTemplateFinished(templateUploadFinishedRequest, options).then((request) => request(axios, basePath));
},
/**
* Blob Storage上のテンプレートファイルのアップロード先アクセスURLを取得します
* @summary
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
uploadTemplateLocation(options?: any): AxiosPromise<TemplateUploadLocationResponse> {
return localVarFp.uploadTemplateLocation(options).then((request) => request(axios, basePath));
},
};
};
@ -4123,6 +4387,29 @@ export class FilesApi extends BaseAPI {
public uploadLocation(options?: AxiosRequestConfig) {
return FilesApiFp(this.configuration).uploadLocation(options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary
* @param {TemplateUploadFinishedRequest} templateUploadFinishedRequest
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof FilesApi
*/
public uploadTemplateFinished(templateUploadFinishedRequest: TemplateUploadFinishedRequest, options?: AxiosRequestConfig) {
return FilesApiFp(this.configuration).uploadTemplateFinished(templateUploadFinishedRequest, options).then((request) => request(this.axios, this.basePath));
}
/**
* Blob Storage上のテンプレートファイルのアップロード先アクセスURLを取得します
* @summary
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof FilesApi
*/
public uploadTemplateLocation(options?: AxiosRequestConfig) {
return FilesApiFp(this.configuration).uploadTemplateLocation(options).then((request) => request(this.axios, this.basePath));
}
}
@ -5357,6 +5644,109 @@ export class TasksApi extends BaseAPI {
/**
* TemplatesApi - axios parameter creator
* @export
*/
export const TemplatesApiAxiosParamCreator = function (configuration?: Configuration) {
return {
/**
*
* @summary
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getTemplates: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/templates`;
// 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: 'GET', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication bearer required
// http bearer authentication required
await setBearerAuthToObject(localVarHeaderParameter, configuration)
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
}
};
/**
* TemplatesApi - functional programming interface
* @export
*/
export const TemplatesApiFp = function(configuration?: Configuration) {
const localVarAxiosParamCreator = TemplatesApiAxiosParamCreator(configuration)
return {
/**
*
* @summary
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getTemplates(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<GetTemplatesResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getTemplates(options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
}
};
/**
* TemplatesApi - factory interface
* @export
*/
export const TemplatesApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
const localVarFp = TemplatesApiFp(configuration)
return {
/**
*
* @summary
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getTemplates(options?: any): AxiosPromise<GetTemplatesResponse> {
return localVarFp.getTemplates(options).then((request) => request(axios, basePath));
},
};
};
/**
* TemplatesApi - object-oriented interface
* @export
* @class TemplatesApi
* @extends {BaseAPI}
*/
export class TemplatesApi extends BaseAPI {
/**
*
* @summary
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof TemplatesApi
*/
public getTemplates(options?: AxiosRequestConfig) {
return TemplatesApiFp(this.configuration).getTemplates(options).then((request) => request(this.axios, this.basePath));
}
}
/**
* UsersApi - axios parameter creator
* @export

View File

@ -16,6 +16,7 @@ import licenseOrderHistory from "features/license/licenseOrderHistory/licenseOrd
import typistGroup from "features/workflow/typistGroup/typistGroupSlice";
import worktype from "features/workflow/worktype/worktypeSlice";
import account from "features/account/accountSlice";
import template from "features/workflow/template/templateSlice";
export const store = configureStore({
reducer: {
@ -36,6 +37,7 @@ export const store = configureStore({
typistGroup,
worktype,
account,
template,
},
});

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.7.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
<style type="text/css">
.st0{fill:#282828;}
</style>
<path class="st0" d="M11,44c-0.8,0-1.5-0.3-2.1-0.9C8.3,42.5,8,41.8,8,41V7c0-0.8,0.3-1.5,0.9-2.1C9.5,4.3,10.2,4,11,4h17l12,12v5.8
h-3V18H26V7H11v34h13v3H11z M11,41V7V41z M33.1,43.9v-6.5h-6.5v-3.9h6.5V27H37v6.5h6.5v3.9H37v6.5H33.1z"/>
</svg>

After

Width:  |  Height:  |  Size: 647 B

View File

@ -1,6 +1,10 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { AccountState } from "./state";
import { updateAccountInfoAsync, getAccountRelationsAsync } from "./operations";
import {
updateAccountInfoAsync,
getAccountRelationsAsync,
deleteAccountAsync,
} from "./operations";
const initialState: AccountState = {
domain: {
@ -95,6 +99,15 @@ export const accountSlice = createSlice({
builder.addCase(updateAccountInfoAsync.rejected, (state) => {
state.apps.isLoading = false;
});
builder.addCase(deleteAccountAsync.pending, (state) => {
state.apps.isLoading = true;
});
builder.addCase(deleteAccountAsync.fulfilled, (state) => {
state.apps.isLoading = false;
});
builder.addCase(deleteAccountAsync.rejected, (state) => {
state.apps.isLoading = false;
});
},
});
export const {

View File

@ -3,7 +3,12 @@ import type { RootState } from "app/store";
import { ErrorObject, createErrorObject } from "common/errors";
import { getTranslationID } from "translation";
import { openSnackbar } from "features/ui/uiSlice";
import { AccountsApi, UpdateAccountInfoRequest, UsersApi } from "../../api/api";
import {
AccountsApi,
UpdateAccountInfoRequest,
UsersApi,
DeleteAccountRequest,
} from "../../api/api";
import { Configuration } from "../../api/configuration";
import { ViewAccountRelationsInfo } from "./types";
@ -72,7 +77,7 @@ export const updateAccountInfoAsync = createAsyncThunk<
const accountApi = new AccountsApi(config);
try {
await accountApi.me(args, {
await accountApi.updateAccountInfo(args, {
headers: { authorization: `Bearer ${accessToken}` },
});
thunkApi.dispatch(
@ -103,3 +108,43 @@ export const updateAccountInfoAsync = createAsyncThunk<
return thunkApi.rejectWithValue({ error });
}
});
export const deleteAccountAsync = createAsyncThunk<
{
/* Empty Object */
},
DeleteAccountRequest,
{
// rejectした時の返却値の型
rejectValue: {
error: ErrorObject;
};
}
>("account/deleteAccountAsync", async (args, thunkApi) => {
const deleteAccounRequest = args;
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const config = new Configuration(configuration);
const accountApi = new AccountsApi(config);
try {
await accountApi.deleteAccount(deleteAccounRequest, {
headers: { authorization: `Bearer ${accessToken}` },
});
return {};
} catch (e) {
const error = createErrorObject(e);
thunkApi.dispatch(
openSnackbar({
level: "error",
message: getTranslationID("common.message.internalServerError"),
})
);
return thunkApi.rejectWithValue({ error });
}
});

View File

@ -250,9 +250,7 @@ export const cancelIssueAsync = createAsyncThunk<
let errorMessage = getTranslationID("common.message.internalServerError");
if (error.code === "E000108") {
errorMessage = getTranslationID("common.message.permissionDeniedError");
} else if (error.code === "E010809") {
if (error.code === "E010809") {
errorMessage = getTranslationID(
"orderHistoriesPage.message.alreadyLicenseStatusChanged"
);

View File

@ -101,10 +101,7 @@ export const getPartnerInfoAsync = createAsyncThunk<
return ret;
} catch (e) {
const error = createErrorObject(e);
const errorMessage =
error.code === "E000108"
? getTranslationID("common.message.permissionDeniedError")
: getTranslationID("common.message.internalServerError");
const errorMessage = getTranslationID("common.message.internalServerError");
thunkApi.dispatch(
openSnackbar({

View File

@ -0,0 +1,3 @@
// TODO 仮で5MBにしているが、OMDS様からの回答待ち
// アップロード可能なファイルサイズの上限(MB)
export const UPLOAD_FILE_SIZE_LIMIT: number = 5 * 1024 * 1024;

View File

@ -0,0 +1,4 @@
export * from "./templateSlice";
export * from "./state";
export * from "./operations";
export * from "./selectors";

View File

@ -0,0 +1,114 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import {
Configuration,
FilesApi,
GetTemplatesResponse,
TemplatesApi,
} from "api";
import type { RootState } from "app/store";
import { ErrorObject, createErrorObject } from "common/errors";
import { openSnackbar } from "features/ui/uiSlice";
import { getTranslationID } from "translation";
import { BlockBlobClient, ContainerClient } from "@azure/storage-blob";
export const listTemplateAsync = createAsyncThunk<
GetTemplatesResponse,
void,
{
// rejectした時の返却値の型
rejectValue: {
error: ErrorObject;
};
}
>("workflow/listTemplateAsync", async (args, thunkApi) => {
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const config = new Configuration(configuration);
const templateApi = new TemplatesApi(config);
try {
const { data } = await templateApi.getTemplates({
headers: { authorization: `Bearer ${accessToken}` },
});
return data;
} catch (e) {
// e ⇒ errorObjectに変換"
const error = createErrorObject(e);
thunkApi.dispatch(
openSnackbar({
level: "error",
message: getTranslationID("common.message.internalServerError"),
})
);
return thunkApi.rejectWithValue({ error });
}
});
export const uploadTemplateAsync = createAsyncThunk<
{
/* Empty Object */
},
void,
{
// rejectした時の返却値の型
rejectValue: {
error: ErrorObject;
};
}
>("workflow/uploadTemplateAsync", async (args, thunkApi) => {
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const { uploadFile } = state.template.apps;
const config = new Configuration(configuration);
const filesApi = new FilesApi(config);
try {
if (!uploadFile) {
throw new Error("uploadFile is not found");
}
// SAS付きのURLを取得する
const { data } = await filesApi.uploadTemplateLocation({
headers: { authorization: `Bearer ${accessToken}` },
});
const { url } = data;
// ファイルをアップロードする
const containerClient = new ContainerClient(url);
const blockBlobClient: BlockBlobClient = containerClient.getBlockBlobClient(
uploadFile.name
);
await blockBlobClient.uploadData(uploadFile);
await filesApi.uploadTemplateFinished(
{
name: uploadFile.name,
url,
},
{
headers: { authorization: `Bearer ${accessToken}` },
}
);
thunkApi.dispatch(
openSnackbar({
level: "info",
message: getTranslationID("common.message.success"),
})
);
return {};
} catch (e) {
// e ⇒ errorObjectに変換"
const error = createErrorObject(e);
thunkApi.dispatch(
openSnackbar({
level: "error",
message: getTranslationID("common.message.internalServerError"),
})
);
return thunkApi.rejectWithValue({ error });
}
});

View File

@ -0,0 +1,29 @@
import { RootState } from "app/store";
import { UPLOAD_FILE_SIZE_LIMIT } from "./constants";
export const selectTemplates = (state: RootState) =>
state.template.domain.templates;
export const selectIsLoading = (state: RootState) =>
state.template.apps.isLoading;
export const selectIsUploading = (state: RootState) =>
state.template.apps.isUploading;
export const selectUploadFile = (state: RootState) =>
state.template.apps.uploadFile;
export const selectUploadFileError = (state: RootState) => {
const { uploadFile } = state.template.apps;
// 必須チェック
if (!uploadFile) {
return { hasErrorRequired: true, hasErrorFileSize: false };
}
// ファイルサイズチェック(5MB)
if (uploadFile.size > UPLOAD_FILE_SIZE_LIMIT) {
return { hasErrorRequired: false, hasErrorFileSize: true };
}
return { hasErrorRequired: false, hasErrorFileSize: false };
};

View File

@ -0,0 +1,16 @@
import { TemplateFile } from "api";
export interface TemplateState {
apps: Apps;
domain: Domain;
}
export interface Apps {
isLoading: boolean;
isUploading: boolean;
uploadFile?: File;
}
export interface Domain {
templates?: TemplateFile[];
}

View File

@ -0,0 +1,53 @@
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
import { TemplateState } from "./state";
import { listTemplateAsync, uploadTemplateAsync } from "./operations";
const initialState: TemplateState = {
apps: {
isLoading: false,
isUploading: false,
uploadFile: undefined,
},
domain: {},
};
export const templateSlice = createSlice({
name: "template",
initialState,
reducers: {
cleanupTemplate: (state) => {
state.apps.uploadFile = initialState.apps.uploadFile;
},
changeUploadFile: (state, action: PayloadAction<{ file: File }>) => {
const { file } = action.payload;
state.apps.uploadFile = file;
},
},
extraReducers: (builder) => {
builder.addCase(listTemplateAsync.pending, (state) => {
state.apps.isLoading = true;
});
builder.addCase(listTemplateAsync.fulfilled, (state, action) => {
const { templates } = action.payload;
state.domain.templates = templates;
state.apps.isLoading = false;
});
builder.addCase(listTemplateAsync.rejected, (state) => {
state.apps.isLoading = false;
});
builder.addCase(uploadTemplateAsync.pending, (state) => {
state.apps.isUploading = true;
});
builder.addCase(uploadTemplateAsync.fulfilled, (state) => {
state.apps.isUploading = false;
});
builder.addCase(uploadTemplateAsync.rejected, (state) => {
state.apps.isUploading = false;
});
},
});
export const { changeUploadFile, cleanupTemplate } = templateSlice.actions;
export default templateSlice.reducer;

View File

@ -0,0 +1,102 @@
import React, { useCallback } from "react";
import { useTranslation } from "react-i18next";
import { AppDispatch } from "app/store";
import { useDispatch, useSelector } from "react-redux";
import styles from "../../styles/app.module.scss";
import { getTranslationID } from "../../translation";
import close from "../../assets/images/close.svg";
import deleteButton from "../../assets/images/delete.svg";
import { selectAccountInfo, selectIsLoading } from "features/account";
import { deleteAccountAsync } from "features/account/operations";
interface deleteAccountPopupProps {
onClose: () => void;
}
export const DeleteAccountPopup: React.FC<deleteAccountPopupProps> = (
props
) => {
const { onClose } = props;
const { t } = useTranslation();
const dispatch: AppDispatch = useDispatch();
const isLoading = useSelector(selectIsLoading);
const accountInfo = useSelector(selectAccountInfo);
// ポップアップを閉じる処理
const closePopup = useCallback(() => {
if (isLoading) {
return;
}
onClose();
}, [isLoading, onClose]);
const onDeleteAccount = useCallback(() => {
dispatch(
deleteAccountAsync({
accountId: accountInfo.account.accountId,
})
);
}, [dispatch]);
// HTML
return (
<div className={`${styles.modal} ${styles.isShow}`}>
<div className={styles.modalBox}>
<p className={styles.modalTitle}>
{t(getTranslationID("deleteAccountPopup.label.title"))}
<button type="button" onClick={closePopup}>
<img src={close} className={styles.modalTitleIcon} alt="close" />
</button>
</p>
<form className={styles.form}>
<dl className={`${styles.formList} ${styles.hasbg}`}>
<dt className={`${styles.formTitle} ${styles.alignCenter}`}>
<p>
<img
src={deleteButton}
alt="delete"
className={styles.formTrash}
/>
</p>
{t(getTranslationID("deleteAccountPopup.label.subTitle"))}
</dt>
<dd
className={`${styles.full} ${styles.alignCenter} ${styles.full}`}
>
<p className={styles.txWsline}>
{t(
getTranslationID(
"deleteAccountPopup.label.cautionOfDeleteingAccountData"
)
)}
</p>
</dd>
<dd className={`${styles.full} ${styles.alignCenter}`}>
<input
type="submit"
name="submit"
value={t(
getTranslationID("deleteAccountPopup.label.deleteButton")
)}
className={`${styles.formDelete} ${styles.marginBtm1} ${styles.isActive}`}
onClick={onDeleteAccount}
/>
<p>
<input
type="button"
name="cancel"
value={t(
getTranslationID("deleteAccountPopup.label.cancelButton")
)}
className={`${styles.formButtonTx} ${styles.marginBtm1}`}
onClick={closePopup}
/>
</p>
</dd>
</dl>
</form>
</div>
</div>
);
};

View File

@ -23,6 +23,7 @@ import { getTranslationID } from "translation";
import { TIERS } from "components/auth/constants";
import { isApproveTier } from "features/auth/utils";
import progress_activit from "../../assets/images/progress_activit.svg";
import { DeleteAccountPopup } from "./deleteAccountPopup";
const AccountPage: React.FC = (): JSX.Element => {
const dispatch: AppDispatch = useDispatch();
@ -36,6 +37,13 @@ const AccountPage: React.FC = (): JSX.Element => {
// ユーザーが第5階層であるかどうかを判定する
const isTier5 = isApproveTier([TIERS.TIER5]);
const [isDeleteAccountPopupOpen, setIsDeleteAccountPopupOpen] =
useState(false);
const onDeleteAccountOpen = useCallback(() => {
setIsDeleteAccountPopupOpen(true);
}, [setIsDeleteAccountPopupOpen]);
// 階層表示用
const tierNames: { [key: number]: string } = {
// eslint-disable-next-line @typescript-eslint/naming-convention
@ -73,277 +81,313 @@ const AccountPage: React.FC = (): JSX.Element => {
}, [dispatch, updateAccountInfo]);
return (
<div className={styles.wrap}>
<Header userName="XXXXXX" />
<UpdateTokenTimer />
<main className={styles.main}>
<div>
<div className={styles.pageHeader}>
<h1 className={styles.pageTitle}>
{t(getTranslationID("accountPage.label.title"))}
</h1>
</div>
<>
{isDeleteAccountPopupOpen && (
<DeleteAccountPopup
onClose={() => {
setIsDeleteAccountPopupOpen(false);
}}
/>
)}
<div className={styles.wrap}>
<Header userName="XXXXXX" />
<UpdateTokenTimer />
<main className={styles.main}>
<div>
<div className={styles.pageHeader}>
<h1 className={styles.pageTitle}>
{t(getTranslationID("accountPage.label.title"))}
</h1>
</div>
<section className={styles.account}>
<div className={styles.boxFlex}>
<ul className={`${styles.menuAction} ${styles.box100}`}>
<li>
<a
href="account_setting.html"
className={`${styles.menuLink} ${styles.isActive}`}
>
<img
src="images/file_delete.svg"
alt=""
className={styles.menuIcon}
/>
{t(getTranslationID("accountPage.label.fileDeleteSetting"))}
</a>
</li>
</ul>
<section className={styles.account}>
<div className={styles.boxFlex}>
<ul className={`${styles.menuAction} ${styles.box100}`}>
<li>
<a
href="account_setting.html"
className={`${styles.menuLink} ${styles.isActive}`}
>
<img
src="images/file_delete.svg"
alt=""
className={styles.menuIcon}
/>
{t(
getTranslationID("accountPage.label.fileDeleteSetting")
)}
</a>
</li>
</ul>
<div className={styles.marginRgt3}>
<dl className={styles.listVertical}>
<h4 className={styles.listHeader}>
{t(
getTranslationID("accountPage.label.accountInformation")
<div className={styles.marginRgt3}>
<dl className={styles.listVertical}>
<h4 className={styles.listHeader}>
{t(
getTranslationID("accountPage.label.accountInformation")
)}
</h4>
<dt>
{t(getTranslationID("accountPage.label.companyName"))}
</dt>
<dd>{viewInfo.account.companyName}</dd>
<dt>
{t(getTranslationID("accountPage.label.accountID"))}
</dt>
<dd>{viewInfo.account.accountId}</dd>
<dt>
{t(getTranslationID("accountPage.label.yourCategory"))}
</dt>
<dd>{tierNames[viewInfo.account.tier]}</dd>
<dt>
{t(getTranslationID("accountPage.label.yourCountry"))}
</dt>
<dd>{viewInfo.account.country}</dd>
<dt>
{t(getTranslationID("accountPage.label.yourDealer"))}
</dt>
{isTier5 && !viewInfo.account.parentAccountName && (
<dd className={styles.form}>
<select
className={`${styles.formInput} ${styles.required}`}
onChange={(event) => {
dispatch(
changeDealer({
parentAccountId:
dealers.find(
(x) => x.name === event.target.value
)?.id || undefined,
})
);
}}
>
<option value="" hidden>
{`-- ${t(
getTranslationID("accountPage.label.selectDealer")
)} --`}
</option>
<option value="">
{`-- ${t(
getTranslationID("common.label.notSelected")
)} --`}
</option>
{dealers.map((x) => (
<option key={x.name} value={x.name}>
{x.name}
</option>
))}
</select>
</dd>
)}
</h4>
<dt>
{t(getTranslationID("accountPage.label.companyName"))}
</dt>
<dd>{viewInfo.account.companyName}</dd>
<dt>{t(getTranslationID("accountPage.label.accountID"))}</dt>
<dd>{viewInfo.account.accountId}</dd>
<dt>
{t(getTranslationID("accountPage.label.yourCategory"))}
</dt>
<dd>{tierNames[viewInfo.account.tier]}</dd>
<dt>
{t(getTranslationID("accountPage.label.yourCountry"))}
</dt>
<dd>{viewInfo.account.country}</dd>
<dt>{t(getTranslationID("accountPage.label.yourDealer"))}</dt>
{isTier5 && !viewInfo.account.parentAccountName && (
{(!isTier5 || viewInfo.account.parentAccountName) && (
<dd>{viewInfo.account.parentAccountName ?? "-"}</dd>
)}
<dt>
{t(
getTranslationID("accountPage.label.dealerManagement")
)}
</dt>
{isTier5 && (
<dd>
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label>
<input
type="checkbox"
className={styles.formCheck}
checked={updateAccountInfo.delegationPermission}
onChange={(e) => {
dispatch(
changeDealerPermission({
delegationPermission: e.target.checked,
})
);
}}
/>
</label>
</dd>
)}
{!isTier5 && <dd>-</dd>}
</dl>
</div>
<div>
<dl className={styles.listVertical}>
<h4 className={styles.listHeader}>
{t(
getTranslationID(
"accountPage.label.administratorInformation"
)
)}
</h4>
<dt>
{t(
getTranslationID(
"accountPage.label.primaryAdministrator"
)
)}
</dt>
<dd>
{
users.find(
(x) => x.id === viewInfo.account.primaryAdminUserId
)?.name
}
</dd>
<dt>
{t(getTranslationID("accountPage.label.emailAddress"))}
</dt>
<dd className={styles.form}>
<select
name=""
className={`${styles.formInput} ${styles.required}`}
onChange={(event) => {
dispatch(
changeDealer({
parentAccountId:
dealers.find(
(x) => x.name === event.target.value
)?.id || undefined,
changePrimaryAdministrator({
primaryAdminUserId:
users.find(
(x) => x.email === event.target.value
)?.id || 0,
})
);
}}
>
<option value="">
{t(
getTranslationID("accountPage.label.selectDealer")
)}
<option
hidden
value={
users.find(
(x) =>
x.id === viewInfo.account.primaryAdminUserId
)?.email || undefined
}
>
{
users.find(
(x) =>
x.id === viewInfo.account.primaryAdminUserId
)?.email
}
</option>
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
<option value="Blank" />
{dealers.map((x) => (
<option key={x.name} value={x.name}>
{x.name}
{users.map((x) => (
<option key={x.email} value={x.email}>
{x.email}
</option>
))}
</select>
{isPushSaveChangesButton && isEmptyPrimaryAdmin && (
<span className={styles.formError}>
{" "}
{t(
getTranslationID(
"signupPage.message.inputEmptyError"
)
)}
</span>
)}
</dd>
)}
{(!isTier5 || viewInfo.account.parentAccountName) && (
<dd>{viewInfo.account.parentAccountName ?? "-"}</dd>
)}
<dt>
{t(getTranslationID("accountPage.label.dealerManagement"))}
</dt>
{isTier5 && (
<dt>
{t(
getTranslationID(
"accountPage.label.secondaryAdministrator"
)
)}
</dt>
<dd>
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label>
<input
type="checkbox"
className={styles.formCheck}
checked={updateAccountInfo.delegationPermission}
onChange={(e) => {
dispatch(
changeDealerPermission({
delegationPermission: e.target.checked,
})
);
}}
/>
</label>
{
users.find(
(x) => x.id === viewInfo.account.secondryAdminUserId
)?.name
}
</dd>
)}
{!isTier5 && <dd>-</dd>}
</dl>
</div>
<div>
<dl className={styles.listVertical}>
<h4 className={styles.listHeader}>
{t(
getTranslationID(
"accountPage.label.administratorInformation"
)
)}
</h4>
<dt>
{t(
getTranslationID("accountPage.label.primaryAdministrator")
)}
</dt>
<dd>
{
users.find(
(x) => x.id === viewInfo.account.primaryAdminUserId
)?.name
}
</dd>
<dt>
{t(getTranslationID("accountPage.label.emailAddress"))}
</dt>
<dd className={styles.form}>
<select
name=""
className={`${styles.formInput} ${styles.required}`}
onChange={(event) => {
dispatch(
changePrimaryAdministrator({
primaryAdminUserId:
users.find((x) => x.email === event.target.value)
?.id || 0,
})
);
}}
>
<option
value={
users.find(
(x) => x.id === viewInfo.account.primaryAdminUserId
)?.email || undefined
}
<dt>
{t(getTranslationID("accountPage.label.emailAddress"))}
</dt>
<dd className={styles.form}>
<select
name=""
className={`${styles.formInput} ${styles.required}`}
onChange={(event) => {
dispatch(
changeSecondryAdministrator({
secondryAdminUserId:
users.find(
(x) => x.email === event.target.value
)?.id ?? undefined,
})
);
}}
>
{
users.find(
(x) => x.id === viewInfo.account.primaryAdminUserId
)?.email
}
</option>
{users.map((x) => (
<option key={x.email} value={x.email}>
{x.email}
</option>
))}
</select>
{isPushSaveChangesButton && isEmptyPrimaryAdmin && (
<span className={styles.formError}>
{" "}
{t(
getTranslationID("signupPage.message.inputEmptyError")
)}
</span>
)}
</dd>
<dt>
{t(
getTranslationID(
"accountPage.label.secondaryAdministrator"
)
)}
</dt>
<dd>
{
users.find(
(x) => x.id === viewInfo.account.secondryAdminUserId
)?.name
}
</dd>
<dt>
{t(getTranslationID("accountPage.label.emailAddress"))}
</dt>
<dd className={styles.form}>
<select
name=""
className={`${styles.formInput} ${styles.required}`}
onChange={(event) => {
dispatch(
changeSecondryAdministrator({
secondryAdminUserId:
users.find((x) => x.email === event.target.value)
?.id ?? undefined,
})
);
}}
>
<option
value={
viewInfo.account.secondryAdminUserId
<option
hidden
value={
viewInfo.account.secondryAdminUserId
? users.find(
(x) =>
x.id ===
viewInfo.account.secondryAdminUserId
)?.email
: undefined
}
>
{viewInfo.account.secondryAdminUserId
? users.find(
(x) =>
x.id === viewInfo.account.secondryAdminUserId
)?.email
: undefined
}
>
{viewInfo.account.secondryAdminUserId
? users.find(
(x) =>
x.id === viewInfo.account.secondryAdminUserId
)?.email
: t(
getTranslationID(
"accountPage.label.selectSecondaryAdministrator"
)
)}
</option>
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
<option value="Blank" />
{users.map((x) => (
<option key={x.email} value={x.email}>
{x.email}
: `-- ${t(
getTranslationID(
"accountPage.label.selectSecondaryAdministrator"
)
)} --`}
</option>
))}
</select>
</dd>
</dl>
</div>
<div className={`${styles.box100} ${styles.alignLeft} `}>
<input
type="submit"
name="submit"
value={t(getTranslationID("accountPage.label.saveChanges"))}
className={`${styles.formSubmit} ${
!isLoading ? styles.isActive : ""
}
<option value="">
{`-- ${t(
getTranslationID("common.label.notSelected")
)} --`}
</option>
{users.map((x) => (
<option key={x.email} value={x.email}>
{x.email}
</option>
))}
</select>
</dd>
</dl>
</div>
<div className={`${styles.box100} ${styles.alignLeft} `}>
<input
type="submit"
name="submit"
value={t(getTranslationID("accountPage.label.saveChanges"))}
className={`${styles.formSubmit} ${
!isLoading ? styles.isActive : ""
}
`}
onClick={onSaveChangesButton}
/>
<img
style={{ display: isLoading ? "inline" : "none" }}
src={progress_activit}
className={styles.icLoading}
alt="Loading"
/>
onClick={onSaveChangesButton}
/>
<img
style={{ display: isLoading ? "inline" : "none" }}
src={progress_activit}
className={styles.icLoading}
alt="Loading"
/>
</div>
</div>
</div>
<ul className={styles.linkBottom}>
<li>
<a href="" className={styles.linkTx}>
{t(getTranslationID("accountPage.label.deleteAccount"))}
</a>
</li>
</ul>
</section>
</div>
</main>
<Footer />
</div>
{isTier5 && (
<ul className={styles.linkBottom}>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a className={styles.linkTx} onClick={onDeleteAccountOpen}>
{t(getTranslationID("accountPage.label.deleteAccount"))}
</a>
</li>
</ul>
)}
</section>
</div>
</main>
<Footer />
</div>
</>
);
};

View File

@ -109,9 +109,7 @@ export const LicenseSummary: React.FC<LicenseSummaryProps> = (
</div>
<section className={styles.license}>
<div>
<h2 className="">
{t(getTranslationID("LicenseSummaryPage.label.subTitle"))}
</h2>
<h2 className="">{"会社名" /* TODO 会社名を表示する */}</h2>
<ul className={styles.menuAction}>
<li>
{/* 他アカウントのライセンス情報を見ている場合は、前画面に戻る用のreturnボタンを表示 */}

View File

@ -159,7 +159,7 @@ export const AddPartnerAccountPopup: React.FC<AddPartnerAccountPopup> = (
}
>
{COUNTRY_LIST.map((x) => (
<option key={x.value} value={x.value}>
<option key={x.value} value={x.value} hidden={!x.value}>
{x.label}
</option>
))}

View File

@ -1,5 +1,5 @@
export const COUNTRY_LIST = [
{ value: "", label: "Select Country" },
{ value: "", label: "-- Select Country --" },
{ value: "CA", label: "Canada" },
{ value: "KY", label: "Cayman Islands" },
{ value: "US", label: "U.S.A." },

View File

@ -140,9 +140,7 @@ const SignupInput: React.FC = (): JSX.Element => {
<h1 className={styles.marginBtm1}>
{t(getTranslationID("signupPage.text.title"))}
</h1>
<p className={styles.marginBtm2}>
{t(getTranslationID("signupPage.text.pageExplanation"))}
</p>
<p className={styles.marginBtm2} />
</div>
<section className={styles.form}>
<form>
@ -190,7 +188,7 @@ const SignupInput: React.FC = (): JSX.Element => {
}
>
{COUNTRY_LIST.map((x) => (
<option key={x.value} value={x.value}>
<option key={x.value} value={x.value} hidden={!x.value}>
{x.label}
</option>
))}
@ -219,15 +217,24 @@ const SignupInput: React.FC = (): JSX.Element => {
}}
value={dealers.find((x) => x.id === dealer)?.id ?? NaN}
>
<option value="">
{`-- ${t(
getTranslationID("common.label.notSelected")
)} --`}
</option>
{[
{
id: NaN,
country: "",
name: "Select dealer",
name: "-- Select dealer --",
},
...dealers,
].map((x) => (
<option key={x.id} value={x.id}>
<option
key={x.id}
value={x.id}
hidden={Number.isNaN(x.id)}
>
{x.name}
</option>
))}

View File

@ -0,0 +1,174 @@
import { AppDispatch } from "app/store";
import {
changeUploadFile,
cleanupTemplate,
listTemplateAsync,
selectIsUploading,
selectUploadFile,
selectUploadFileError,
uploadTemplateAsync,
} from "features/workflow/template";
import React, { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useDispatch, useSelector } from "react-redux";
import styles from "styles/app.module.scss";
import { getTranslationID } from "translation";
import close from "../../assets/images/close.svg";
import progress_activit from "../../assets/images/progress_activit.svg";
interface AddTemplateFilePopupProps {
onClose: () => void;
}
export const AddTemplateFilePopup: React.FC<AddTemplateFilePopupProps> = (
props: AddTemplateFilePopupProps
) => {
const { onClose } = props;
const [t] = useTranslation();
const dispatch: AppDispatch = useDispatch();
// 保存ボタンを押したかどうか
const [isPushUploadButton, setIsPushUploadButton] = useState<boolean>(false);
// アップロード対象のファイル情報
const uploadFile = useSelector(selectUploadFile);
// ファイルアップロード中かどうか
const isUploading = useSelector(selectIsUploading);
// ファイルのエラー
const { hasErrorFileSize, hasErrorRequired } = useSelector(
selectUploadFileError
);
// ブラウザのウィンドウが閉じられようとしている場合に発火するイベントハンドラ
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
// テンプレートファイルアップロード中に閉じられようとしている場合、ダイアログを表示させる
if (isUploading) {
e.preventDefault();
// ChromeではreturnValueが必要
e.returnValue = "";
}
};
// ファイルが選択されたときの処理
const handleFileChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
// 選択されたファイルを取得(複数選択されても先頭を取得)
const file = event.target.files?.[0];
// ファイルが選択されていれば、storeに保存
if (file) {
dispatch(changeUploadFile({ file }));
}
// 同名のファイルを選択した場合、onChangeが発火しないため、valueをクリアする
event.target.value = "";
},
[dispatch]
);
// ファイルアップロード処理
const handleUploadFile = useCallback(async () => {
setIsPushUploadButton(true);
// エラーチェックを実施
if (hasErrorFileSize || hasErrorRequired) {
return;
}
// ファイルアップロード処理
const { meta } = await dispatch(uploadTemplateAsync());
if (meta.requestStatus === "fulfilled") {
onClose();
dispatch(listTemplateAsync());
}
}, [dispatch, hasErrorFileSize, hasErrorRequired, onClose]);
// コンポーネントがマウントされた時にイベントハンドラを登録する
useEffect(() => {
window.addEventListener("beforeunload", handleBeforeUnload);
// コンポーネントがアンマウントされるときにイベントハンドラを解除する
return () => {
window.removeEventListener("beforeunload", handleBeforeUnload);
};
});
useEffect(
() => () => {
// useEffectのreturnとしてcleanupAppsを実行することで、ポップアップのアンマウント時に初期化を行う
dispatch(cleanupTemplate());
setIsPushUploadButton(false);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
);
return (
<div className={`${styles.modal} ${styles.isShow}`}>
<div className={styles.modalBox}>
<p className={styles.modalTitle}>
{t(getTranslationID("templateFilePage.label.addTemplate"))}
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions */}
<img
src={close}
className={styles.modalTitleIcon}
alt="close"
onClick={onClose}
/>
</p>
<form className={styles.form}>
<dl className={`${styles.formList} ${styles.hasbg}`}>
<dt className={styles.formTitle} />
<dd
className={`${styles.full} ${styles.alignCenter} ${styles.last}`}
>
<p className={styles.formFileName} style={{ marginRight: "5px" }}>
{uploadFile?.name ??
t(getTranslationID("templateFilePage.label.notFileChosen"))}
</p>
<label htmlFor="template" className={styles.formFileButton}>
{t(getTranslationID("templateFilePage.label.chooseFile"))}
<input
type="file"
id="template"
className={`${styles.formInput} ${styles.isHide}`}
onChange={handleFileChange}
/>
</label>
{isPushUploadButton && hasErrorRequired && (
<span className={`${styles.formError} ${styles.alignCenter}`}>
{t(getTranslationID("templateFilePage.label.fileEmptyError"))}
</span>
)}
{isPushUploadButton && hasErrorFileSize && (
<span
className={`${styles.formError} ${styles.alignCenter}`}
style={{ margin: "0px 30px 0px 30px" }}
>
{t(getTranslationID("templateFilePage.label.fileSizeError"))}
</span>
)}
</dd>
<dd className={`${styles.full} ${styles.alignCenter}`}>
<input
type="button"
name="submit"
value={t(
getTranslationID("templateFilePage.label.addTemplate")
)}
onClick={handleUploadFile}
className={`${styles.formSubmit} ${styles.marginBtm1} ${
!isUploading ? styles.isActive : ""
}`}
/>
{isUploading && (
<img
src={progress_activit}
className={styles.icLoading}
alt="Loading"
/>
)}
</dd>
</dl>
</form>
</div>
</div>
);
};

View File

@ -0,0 +1,128 @@
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { AppDispatch } from "app/store";
import Header from "components/header";
import { UpdateTokenTimer } from "components/auth/updateTokenTimer";
import { useTranslation } from "react-i18next";
import { getTranslationID } from "translation";
import undo from "assets/images/undo.svg";
import styles from "styles/app.module.scss";
import addTemplate from "assets/images/template_add.svg";
import progress_activit from "assets/images/progress_activit.svg";
import {
selectTemplates,
listTemplateAsync,
selectIsLoading,
} from "features/workflow/template";
import { AddTemplateFilePopup } from "./addTemplateFilePopup";
export const TemplateFilePage: React.FC = () => {
const dispatch: AppDispatch = useDispatch();
const [t] = useTranslation();
const templates = useSelector(selectTemplates);
const isLoading = useSelector(selectIsLoading);
// 追加Popupの表示制御
const [isShowAddPopup, setIsShowAddPopup] = useState<boolean>(false);
useEffect(() => {
dispatch(listTemplateAsync());
}, [dispatch]);
return (
<>
{isShowAddPopup && (
<AddTemplateFilePopup
onClose={() => {
setIsShowAddPopup(false);
}}
/>
)}
<div className={styles.wrap}>
<Header userName="XXXXXXXX" />
<UpdateTokenTimer />
<main className={styles.main}>
<div>
<div className={styles.pageHeader}>
<h1 className={styles.pageTitle}>
{t(getTranslationID("workflowPage.label.title"))}
</h1>
<p className={styles.pageTx}>
{t(getTranslationID("templateFilePage.label.title"))}
</p>
</div>
</div>
<section className={styles.workflow}>
<div>
<ul className={`${styles.menuAction} ${styles.worktype}`}>
<li>
<a
href="/workflow"
className={`${styles.menuLink} ${styles.isActive}`}
>
<img src={undo} alt="" className={styles.menuIcon} />
{t(getTranslationID("common.label.return"))}
</a>
</li>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
className={`${styles.menuLink} ${styles.isActive}`}
onClick={() => {
setIsShowAddPopup(true);
}}
>
<img src={addTemplate} alt="" className={styles.menuIcon} />
{t(getTranslationID("templateFilePage.label.addTemplate"))}
</a>
</li>
</ul>
<table className={`${styles.table} ${styles.template}`}>
<tr className={styles.tableHeader}>
<th className={styles.noLine}>
{t(getTranslationID("templateFilePage.label.fileName"))}
</th>
<th>{/** empty th */}</th>
</tr>
{templates?.map((template) => (
<tr key={template.id}>
<td>{template.name}</td>
<td>
<ul className={`${styles.menuAction} ${styles.inTable}`}>
<li>
<a
href=""
className={`${styles.menuLink} ${styles.isActive}`}
>
{t(getTranslationID("common.label.delete"))}
</a>
</li>
</ul>
</td>
</tr>
))}
{!isLoading && templates?.length === 0 && (
<p
style={{
margin: "10px",
textAlign: "center",
}}
>
{t(getTranslationID("common.message.listEmpty"))}
</p>
)}
{isLoading && (
<img
src={progress_activit}
className={styles.icLoading}
alt="Loading"
/>
)}
</table>
</div>
</section>
</main>
</div>
</>
);
};

View File

@ -102,13 +102,7 @@ export const AllocateLicensePopup: React.FC<AllocateLicensePopupProps> = (
</p>
<form action="" name="" method="" className={styles.form}>
<dl className={`${styles.formList} ${styles.hasbg}`}>
<dt className={styles.formTitle}>
{t(
getTranslationID(
"allocateLicensePopupPage.label.personalInformation"
)
)}
</dt>
<dt className={styles.formTitle} />
<dt>
{t(getTranslationID("allocateLicensePopupPage.label.email"))}
</dt>
@ -224,12 +218,13 @@ export const AllocateLicensePopup: React.FC<AllocateLicensePopupProps> = (
}}
value={selectedlicenseId ?? ""}
>
<option value="">
{t(
<option value="" hidden>
{`--
${t(
getTranslationID(
"allocateLicensePopupPage.label.dropDownHeading"
)
)}
)} --`}
</option>
{allocatableLicenses.map((x) => (
<option key={x.licenseId} value={x.licenseId}>

View File

@ -54,6 +54,7 @@ const WorktypeIdSettingPage: React.FC = (): JSX.Element => {
setSelectedActiveWorktypeId(activeWorktypeId);
}, [activeWorktypeId]);
// TODO:このsetTimeoutを使った実装は暫定対応。useEffectで無理やり処理しているので修正予定
// ActiveWorktypeIDのセレクトボックス変更時セレクトボックスに表示するActiveWorktypeIDの変更時
useEffect(() => {
// 画面表示の変更後にダイアログを表示するため、setTimeoutを使用
@ -150,7 +151,7 @@ const WorktypeIdSettingPage: React.FC = (): JSX.Element => {
)}
</a>
</li>
<li className={styles.selectMenu}>
<li className={`${styles.selectMenu} ${styles.form}`}>
{`${t(
getTranslationID(
"worktypeIdSetting.label.activeWorktypeId"
@ -165,9 +166,13 @@ const WorktypeIdSettingPage: React.FC = (): JSX.Element => {
const active = value === "" ? undefined : Number(value);
setSelectedActiveWorktypeId(active);
}}
required
>
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
<option value="" />
<option value="">
{`-- ${t(
getTranslationID("common.label.notSelected")
)} --`}
</option>
{worktypes?.map((worktype) => (
<option key={worktype.id} value={worktype.id}>
{worktype.worktypeId}

View File

@ -20,6 +20,11 @@ const WorkflowPage: React.FC = (): JSX.Element => (
Worktype ID Setting
</a>
</span>
<span>
<a style={{ margin: 20 }} href="/workflow/template">
Template File
</a>
</span>
</div>
</main>
<Footer />

View File

@ -868,9 +868,7 @@ h3 + .brCrumb .tlIcon {
.listVertical dd {
width: 42%;
text-align: right;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
}
.boxFlex {
@ -1543,136 +1541,15 @@ _:-ms-lang(x)::-ms-backdrop,
padding-bottom: 2rem;
vertical-align: top;
}
.account.account .listVertical,
.user.account .listVertical,
.license.account .listVertical,
.dictation.account .listVertical,
.partners.account .listVertical,
.workflow.account .listVertical {
.account .listVertical {
margin-bottom: 3rem;
}
.account.account .listVertical dd .formInput,
.user.account .listVertical dd .formInput,
.license.account .listVertical dd .formInput,
.dictation.account .listVertical dd .formInput,
.partners.account .listVertical dd .formInput,
.workflow.account .listVertical dd .formInput {
.account .listVertical dd .formInput {
max-width: 100%;
}
.account.account .listVertical dd .formCheckToggle,
.user.account .listVertical dd .formCheckToggle,
.license.account .listVertical dd .formCheckToggle,
.dictation.account .listVertical dd .formCheckToggle,
.partners.account .listVertical dd .formCheckToggle,
.workflow.account .listVertical dd .formCheckToggle {
position: relative;
cursor: pointer;
}
.account.account .listVertical dd .formCheckToggle .toggleBase,
.user.account .listVertical dd .formCheckToggle .toggleBase,
.license.account .listVertical dd .formCheckToggle .toggleBase,
.dictation.account .listVertical dd .formCheckToggle .toggleBase,
.partners.account .listVertical dd .formCheckToggle .toggleBase,
.workflow.account .listVertical dd .formCheckToggle .toggleBase {
display: inline-block;
width: 2.8rem;
height: 1.6rem;
margin-right: 0.5rem;
border-radius: 0.8rem;
background: #e6e6e6;
vertical-align: bottom;
position: relative;
-moz-transition: all 0.3s ease-out;
-ms-transition: all 0.3s ease-out;
-webkit-transition: all 0.3s ease-out;
transition: all 0.3s ease-out;
}
.account.account .listVertical dd .formCheckToggle .toggleBase::after,
.user.account .listVertical dd .formCheckToggle .toggleBase::after,
.license.account .listVertical dd .formCheckToggle .toggleBase::after,
.dictation.account .listVertical dd .formCheckToggle .toggleBase::after,
.partners.account .listVertical dd .formCheckToggle .toggleBase::after,
.workflow.account .listVertical dd .formCheckToggle .toggleBase::after {
content: "";
width: 1.3rem;
height: 1.3rem;
border-radius: 50%;
background: #999999;
position: absolute;
top: 0.15rem;
left: 0.15rem;
-moz-transition: all 0.15s ease-out;
-ms-transition: all 0.15s ease-out;
-webkit-transition: all 0.15s ease-out;
transition: all 0.15s ease-out;
}
.account.account .listVertical dd .formCheckToggle input,
.user.account .listVertical dd .formCheckToggle input,
.license.account .listVertical dd .formCheckToggle input,
.dictation.account .listVertical dd .formCheckToggle input,
.partners.account .listVertical dd .formCheckToggle input,
.workflow.account .listVertical dd .formCheckToggle input {
position: absolute;
width: 0;
heigh: 0;
}
.account.account .listVertical dd .formCheckToggle input:checked ~ .toggleBase,
.user.account .listVertical dd .formCheckToggle input:checked ~ .toggleBase,
.license.account .listVertical dd .formCheckToggle input:checked ~ .toggleBase,
.dictation.account
.listVertical
dd
.formCheckToggle
input:checked
~ .toggleBase,
.partners.account .listVertical dd .formCheckToggle input:checked ~ .toggleBase,
.workflow.account
.listVertical
dd
.formCheckToggle
input:checked
~ .toggleBase {
background: #c4eeec;
}
.account.account
.listVertical
dd
.formCheckToggle
input:checked
~ .toggleBase::after,
.user.account
.listVertical
dd
.formCheckToggle
input:checked
~ .toggleBase::after,
.license.account
.listVertical
dd
.formCheckToggle
input:checked
~ .toggleBase::after,
.dictation.account
.listVertical
dd
.formCheckToggle
input:checked
~ .toggleBase::after,
.partners.account
.listVertical
dd
.formCheckToggle
input:checked
~ .toggleBase::after,
.workflow.account
.listVertical
dd
.formCheckToggle
input:checked
~ .toggleBase::after {
background: #00b4aa;
top: 0.15rem;
left: 1.35rem;
.account .box100.alignRight {
width: calc(1200px + 3rem);
}
.menuAction {
@ -2332,8 +2209,7 @@ tr.isSelected .menuInTable li a.isDisable {
}
.formChange ul.chooseMember li input + label:hover,
.formChange ul.holdMember li input + label:hover {
background: #e6e6e6 url(../assets/images/arrow_circle_left.svg) no-repeat left
center;
background: #e6e6e6 url(../assets/images/arrow_circle_left.svg) no-repeat left center;
background-size: 1.3rem;
}
.formChange ul.chooseMember li input:checked + label,
@ -2344,8 +2220,8 @@ tr.isSelected .menuInTable li a.isDisable {
}
.formChange ul.chooseMember li input:checked + label:hover,
.formChange ul.holdMember li input:checked + label:hover {
background: #e6e6e6 url(../assets/images/arrow_circle_right.svg) no-repeat
right center;
background: #e6e6e6 url(../assets/images/arrow_circle_right.svg) no-repeat right
center;
background-size: 1.3rem;
}
.formChange > p {
@ -2498,8 +2374,7 @@ tr.isSelected .menuInTable li a.isDisable {
}
.formChange ul.chooseMember li input + label:hover,
.formChange ul.holdMember li input + label:hover {
background: #e6e6e6 url(../assets/images/arrow_circle_left.svg) no-repeat left
center;
background: #e6e6e6 url(../assets/images/arrow_circle_left.svg) no-repeat left center;
background-size: 1.3rem;
}
.formChange ul.chooseMember li input:checked + label,
@ -2510,8 +2385,8 @@ tr.isSelected .menuInTable li a.isDisable {
}
.formChange ul.chooseMember li input:checked + label:hover,
.formChange ul.holdMember li input:checked + label:hover {
background: #e6e6e6 url(../assets/images/arrow_circle_right.svg) no-repeat
right center;
background: #e6e6e6 url(../assets/images/arrow_circle_right.svg) no-repeat right
center;
background-size: 1.3rem;
}
.formChange > p {

View File

@ -22,6 +22,7 @@ declare const classNames: {
readonly buttonText: "buttonText";
readonly formList: "formList";
readonly formTitle: "formTitle";
readonly alignCenter: "alignCenter";
readonly overLine: "overLine";
readonly full: "full";
readonly hasbg: "hasbg";
@ -38,10 +39,14 @@ declare const classNames: {
readonly formError: "formError";
readonly formConfirm: "formConfirm";
readonly formSubmit: "formSubmit";
readonly formButtonFul: "formButtonFul";
readonly formButton: "formButton";
readonly formDelete: "formDelete";
readonly formBack: "formBack";
readonly formButtonTx: "formButtonTx";
readonly formDone: "formDone";
readonly formDelete: "formDelete";
readonly formTrash: "formTrash";
readonly listVertical: "listVertical";
readonly listHeader: "listHeader";
readonly boxFlex: "boxFlex";
@ -109,6 +114,11 @@ declare const classNames: {
readonly isDisable: "isDisable";
readonly icCheckCircle: "icCheckCircle";
readonly icInTable: "icInTable";
readonly manage: "manage";
readonly manageInfo: "manageInfo";
readonly txNormal: "txNormal";
readonly manageIcon: "manageIcon";
readonly manageIconClose: "manageIconClose";
readonly history: "history";
readonly cardHistory: "cardHistory";
readonly partner: "partner";
@ -116,6 +126,7 @@ declare const classNames: {
readonly displayOptions: "displayOptions";
readonly tableFilter: "tableFilter";
readonly tableFilter2: "tableFilter2";
readonly mnBack: "mnBack";
readonly txWsline: "txWsline";
readonly hidePri: "hidePri";
readonly opPri: "opPri";
@ -175,6 +186,7 @@ declare const classNames: {
readonly op9: "op9";
readonly hideO10: "hideO10";
readonly op10: "op10";
readonly property: "property";
readonly formChange: "formChange";
readonly chooseMember: "chooseMember";
readonly holdMember: "holdMember";
@ -184,7 +196,6 @@ declare const classNames: {
readonly template: "template";
readonly worktype: "worktype";
readonly selectMenu: "selectMenu";
readonly alignCenter: "alignCenter";
readonly alignLeft: "alignLeft";
readonly alignRight: "alignRight";
readonly floatNone: "floatNone";
@ -206,7 +217,6 @@ declare const classNames: {
readonly paddSide1: "paddSide1";
readonly paddSide2: "paddSide2";
readonly paddSide3: "paddSide3";
readonly txNormal: "txNormal";
readonly txIcon: "txIcon";
readonly txWswrap: "txWswrap";
readonly required: "required";

View File

@ -1,399 +1,395 @@
{
"common": {
"message": {
"inputEmptyError": "(de)この項目の入力は必須です。入力してください。",
"passwordIncorrectError": "(de)Error Message",
"emailIncorrectError": "(de)Error Message",
"internalServerError": "(de)処理に失敗しました。時間をおいて再実行しても解決しない場合はシステム管理者にお問い合わせください。",
"permissionDeniedError": "(de)操作を実行する権限がありません。",
"listEmpty": "(de)検索結果が0件です。",
"dialogConfirm": "(de)操作を実行しますか?",
"success": "(de)処理に成功しました。"
"inputEmptyError": "Pflichtfeld",
"passwordIncorrectError": "(de)入力されたパスワードがルールを満たしていません。下記のルールを満たすパスワードを入力してください。",
"emailIncorrectError": "(de)メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
"internalServerError": "Verarbeitung fehlgeschlagen. Bitte versuchen Sie es später noch einmal.",
"listEmpty": "Es gibt 0 Suchergebnisse.",
"dialogConfirm": "Möchten Sie die Operation durchführen?",
"success": "Erfolgreich verarbeitet"
},
"label": {
"cancel": "(de)Cancel",
"headerTitle": "(de)ODMS Cloud",
"copyRight": "(de)OM Digital Solutions 2023",
"edit": "(de)Edit",
"save": "(de)Save",
"delete": "(de)Delete",
"return": "(de)Return",
"cancel": "Abbrechen",
"copyRight": "© OM Digital Solutions Corporation",
"edit": "Bearbeiten",
"save": "Speichern",
"delete": "Löschen",
"return": "zurückkehren",
"tier1": "(de)Admin",
"tier2": "(de)BC",
"tier3": "(de)Distributor",
"tier4": "(de)Dealer",
"tier5": "(de)Customer"
"tier5": "(de)Customer",
"notSelected": "(de)None"
}
},
"topPage": {
"message": {
"logout": "(de)ログイン有効期限が切れました。再度ログイン手続きを行ってください。"
"logout": "Ihr Login ist abgelaufen. Bitte melden Sie sich erneut an."
},
"label": {
"displayLanguage": "(de)Display language",
"languageEnglish": "(de)English",
"languageGerman": "(de)German",
"languageSpanish": "(de)Spanish",
"languageFrench": "(de)French",
"alreadyHaveAccount": "(de)Already have an account?",
"signInButton": "(de)Sign in",
"newUser": "(de)New user?",
"signUpButton": "(de)Create a new account",
"displayLanguage": "Sprache",
"languageEnglish": "English",
"languageGerman": "Deutsch",
"languageSpanish": "Español",
"languageFrench": "Français",
"alreadyHaveAccount": "Existierender Benutzer",
"signInButton": "Anmelden",
"newUser": "Neuer Benutzer",
"signUpButton": "Benutzerkonto erstellen",
"logoAlt": "(de)OM Dictation Management System in the Cloud"
}
},
"signupPage": {
"message": {
"inputEmptyError": "(de)この項目の入力は必須です。入力してください。",
"passwordIncorrectError": "(de)入力されたパスワードがルールを満たしていません。下記のルールを満たすパスワードを入力してください。",
"emailIncorrectError": "(de)メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
"dealerNotFoundError": "(de)指定されたディーラーが見つかりませんでした。直接ディーラーを指定してください。"
"inputEmptyError": "Pflichtfeld",
"passwordIncorrectError": "Das von Ihnen eingegebene Passwort entspricht nicht den Regeln. Bitte geben Sie ein Passwort ein, das den folgenden Regeln entspricht.",
"emailIncorrectError": "Das Format der E-Mail-Adresse ist ungültig. Bitte geben Sie ein gültiges E-Mail-Adressformat ein. (Das System erkennt ungültige E-Mails, B. ohne ein -Zeichen, oder der Teil mit .com sieht nicht korrekt aus.)",
"dealerNotFoundError": "In der Händlerauswahl ist ein falscher Wert enthalten. Bitte wählen Sie erneut einen Händler aus."
},
"text": {
"title": "(de)Create your account",
"pageExplanation": "(de)Explanation...",
"accountInfoTitle": "(de)Register your account information",
"countryExplanation": "(de) Please select your country or the nearest country.",
"dealerExplanation": "(de)Please select the dealer to purchase a license.",
"adminInfoTitle": "(de)Register primary administrator's information",
"passwordTerms": "(de)Please set a password or issue an initial password.\nThe password must be more than 8 or less than 64 letters,numbers and symbols."
"title": "Erstelle deinen Account",
"accountInfoTitle": "Informationen zur Anmeldung",
"countryExplanation": "Wählen Sie das Land aus, in dem Sie sich befinden. Wenn Ihr Land nicht aufgeführt ist, wählen Sie bitte das nächstgelegene Land aus.",
"dealerExplanation": "Bitte wählen Sie den Händler aus, bei dem Sie die Lizenz erwerben möchten.",
"adminInfoTitle": "Registrieren Sie die Informationen des primären Administrators",
"passwordTerms": "Bitte legen Sie ein Passwort fest. Das Passwort muss 825 Zeichen lang sein und Buchstaben, Zahlen und Symbole enthalten. (Sollte ein kompatibles Symbol auflisten und angeben, ob ein Großbuchstabe erforderlich ist.)"
},
"label": {
"company": "(de)Company Name",
"country": "(de)Country",
"dealer": "(de)Dealer",
"adminName": "(de)Admin Name",
"email": "(de)Email",
"password": "(de)Password",
"termsLink": "(de)Click here to read the terms of use",
"company": "Name der Firma",
"country": "Land",
"dealer": "Händler (Optional)",
"adminName": "Name des Administrators",
"email": "E-Mail-Addresse",
"password": "Passwort",
"termsLink": "Klicken Sie hier, um die Nutzungsbedingungen zu lesen.",
"termsLinkFor": "(de)for ODDS.",
"termsCheckBox": "(de)Yes, I agree to the terms of use.",
"createAccountButton": "(de)Create account"
"termsCheckBox": "Ja, ich stimme den Nutzungsbedingungen zu.",
"createAccountButton": "Einreichen"
}
},
"signupConfirmPage": {
"message": {
"emailConflictError": "(de)このメールアドレスは既に登録されています。他のメールアドレスで登録してください。"
"emailConflictError": "Diese Emailadresse ist bereits registriert. Bitte registrieren Sie sich mit einer anderen E-Mail-Adresse."
},
"text": {
"title": "(de)Confirmation",
"pageExplanation": "(de)Explanation ......",
"accountInfoTitle": "(de)Your account information",
"adminInfoTitle": "(de)Primary administrator's information"
"title": "Überprüfung",
"pageExplanation": "Bitte überprüfen Sie, ob die folgenden Informationen korrekt sind. Klicken Sie dann auf Registrieren.",
"accountInfoTitle": "Ihre Kontoinformationen",
"adminInfoTitle": "Informationen des primären Administrators"
},
"label": {
"company": "(de)Company Name",
"country": "(de)Country",
"dealer": "(de)Dealer (Optional)",
"adminName": "(de)Admin Name",
"email": "(de)Email",
"password": "(de)Password",
"signupButton": "(de)Sign up"
"company": "Name der Firma",
"country": "Land",
"dealer": "Händler (Optional)",
"adminName": "Name des Administrators",
"email": "E-Mail-Addresse",
"password": "Passwort",
"signupButton": "Registrieren"
}
},
"signupCompletePage": {
"text": {
"createdInfo": "(de)Your account has been created and a verification email has been set to your registered email address. Please click on the verification link included in the email to activate your account."
"createdInfo": "Ihr Konto wurde erstellt und eine Bestätigungs-E-Mail wurde an Ihre registrierte E-Mail-Adresse gesendet. Bitte klicken Sie auf den Bestätigungslink in der E-Mail, um Ihr Konto zu aktivieren."
},
"label": {
"title": "(de)Account created"
"title": "Account erstellt"
}
},
"signupVerifyPage": {
"text": {
"verifySuccess": "(de)You have successfully verified the account.",
"faild": "(de)The verification url does not match. Please try again,\nor click on the link below to receive a new verification mail.",
"alreadySuccess": "(de)Your account has already been verified."
"verifySuccess": "Ihr Konto wurde erfolgreich verifiziert.",
"faild": "Verifizierung fehlgeschlagen. Klicken Sie bitte auf den untenstehenden Link, um die Bestätigungs-E-Mail erneut zu senden.",
"alreadySuccess": "Ihr Konto wurde bereits verifiziert."
},
"label": {
"verifySuccess": "(de)Verified!",
"faild": "(de)Verification failed",
"alreadySuccess": "(de)Already Verified!",
"returnToSignIn": "(de)Return to Sign in"
"verifySuccess": "Verifiziert!",
"faild": "Verifizierung fehlgeschlagen",
"alreadySuccess": "Bereits verifiziert!",
"returnToSignIn": "Klicken Sie zum Anmelden"
}
},
"userListPage": {
"message": {
"addUserSuccess": "(de)メールアドレス宛に認証用メールを送信しました。",
"authorIdConflictError": "(de)このAuthor IDは既に登録されています。他のAuthor IDで登録してください。",
"authorIdIncorrectError": "(de)Author IDの形式が不正です。Author IDは半角英数字(大文字)と\"_\"のみ入力可能です。",
"roleChangeError": "(de)Roleの変更に失敗しました。画面を更新して再度ユーザー情報を取得してください。",
"encryptionPasswordCorrectError": "(de)EncryptionPasswordがルールを満たしていません。",
"alreadyLicenseDeallocatedError": "(de)すでにライセンス割り当てが解除されています。画面を更新して再度ご確認下さい。"
"addUserSuccess": "Eine Bestätigungs-E-Mail wurde an Ihre E-Mail-Adresse gesendet.",
"authorIdConflictError": "Diese Autoren-ID wurde bereits registriert. Bitte registrieren Sie sich mit einer anderen Autoren-ID.",
"authorIdIncorrectError": "Das Format der Autoren-ID ist ungültig. Als Autoren-ID können nur alphanumerische Zeichen und „_“ eingegeben werden.",
"roleChangeError": "Die Benutzerrolle kann nicht geändert werden. Die angezeigten Informationen sind möglicherweise veraltet. Aktualisieren Sie daher bitte den Bildschirm, um den neuesten Status anzuzeigen.",
"encryptionPasswordCorrectError": "Das Verschlüsselungskennwort entspricht nicht den Regeln.",
"alreadyLicenseDeallocatedError": "Die zugewiesene Lizenz wurde bereits storniert. Die angezeigten Informationen sind möglicherweise veraltet. Aktualisieren Sie daher bitte den Bildschirm, um den neuesten Status anzuzeigen."
},
"label": {
"title": "(de)User",
"addUser": "(de)Add User",
"licenseAllocation": "(de)License Allocation",
"name": "(de)Name",
"role": "(de)Role",
"authorID": "(de)Author ID",
"typistGroup": "(de)Typist Group",
"email": "(de)Email",
"status": "(de)Status",
"expiration": "(de)Expiration",
"remaining": "(de)Remaining",
"autoRenew": "(de)Auto renew",
"licenseAlert": "(de)License alert",
"notification": "(de)Notification",
"users": "(de)users",
"of": "(de)of",
"personal": "(de)Personal information",
"setting": "(de)Setting",
"selectGroup": "(de)Select group",
"addToGroup": "(de)Add to group (Optional)",
"author": "(de)Author",
"transcriptionist": "(de)Transcriptionist",
"encryption": "(de)Encryption",
"prompt": "(de)Prompt",
"emailVerified": "(de)Email Verified",
"editUser": "(de)Edit User",
"licenseDeallocation": "(de)License Deallocation",
"deleteUser": "(de)Delete User",
"none": "(de)None",
"encryptionPassword": "(de)Password:",
"encryptionPasswordTerm": "(de)Please set your password using 4 to 16 ASCII characters.\nYour password may include letters, numbers, and symbols."
"title": "Benutzer",
"addUser": "Benutzer hinzufügen",
"licenseAllocation": "Lizenz zuweisen",
"name": "Name",
"role": "Rolle",
"authorID": "Autoren-ID",
"typistGroup": "Transkriptionistengruppe",
"email": "Email",
"status": "Status",
"expiration": "Verfallsdatum",
"remaining": "Verbleibender Zeitraum",
"autoRenew": "Automatische Erneuerung",
"licenseAlert": "Lizenzalarm",
"notification": "Benachrichtigung",
"users": "Benutzer",
"of": "von",
"personal": "Benutzerinformationen",
"setting": "Einstellungen",
"selectGroup": "Wähle die Gruppe",
"addToGroup": "Gruppe hinzufügen (optional)",
"author": "Autor",
"transcriptionist": "Transkriptionist",
"encryption": "Verschlüsselung",
"prompt": "Eingabeaufforderung",
"emailVerified": "Email Verifiziert",
"editUser": "Benutzer bearbeiten",
"licenseDeallocation": "Lizenzzuweisung aufheben",
"deleteUser": "Benutzer löschen",
"none": "Keiner",
"encryptionPassword": "Passwort",
"encryptionPasswordTerm": "Bitte legen Sie Ihr Passwort mit 4 bis 16 alphanumerischen Zeichen und Symbolen fest."
}
},
"LicenseSummaryPage": {
"label": {
"title": "(de)License",
"subTitle": "(de)EFGI Legal",
"orderLicense": "(de)Order License",
"orderHistory": "(de)Order History",
"activateLicenseKey": "(de)Activate License Key",
"totalLicense": "(de)Total license",
"allocatedLicense": "(de)Allocated license",
"reusableLicense": "(de)Reusable license",
"freeLicense": "(de)Free license",
"expiringWithin14daysLicense": "(de)Expiring within 14days license",
"issueRequesting": "(de)Issue Requesting",
"numberOfRequesting": "(de)Number of Requesting",
"shortage": "(de)Shortage",
"storageSize": "(de)Storage Size",
"usedSize": "(de)Used Size",
"storageAvailable": "(de)Storage Available"
"title": "Lizenz(en)",
"orderLicense": "Lizenz bestellen",
"orderHistory": "Bestellverlauf",
"activateLicenseKey": "Lizenzschlüssel aktivieren",
"totalLicense": "Lizenzinventar",
"allocatedLicense": "Anzahl der zugewiesenen Lizenzen",
"reusableLicense": "Anzahl der zur Wiederverwendung verfügbaren Lizenzen",
"freeLicense": "Anzahl ungenutzter Lizenzen",
"expiringWithin14daysLicense": "Anzahl der Lizenzen, die innerhalb von 14 Tagen ablaufen",
"issueRequesting": "Gesamtzahl der bestellten Lizenzen",
"numberOfRequesting": "Gesamtzahl der Bestellungen",
"shortage": "Mangel",
"storageSize": "Lagerung verfügbar",
"usedSize": "Gebrauchter Lagerung",
"storageAvailable": "Speicher nicht verfügbar (Menge überschritten)"
}
},
"licenseOrderPage": {
"message": {
"inputEmptyError": "(de)この項目の入力は必須です。入力してください。",
"poNumberIncorrectError": "(de)PO Numberの形式が不正です。PO Numberは半角英数字(大文字)のみ入力可能です。",
"newOrderIncorrectError": "(de)New Orderには1以上の数字を入力してください。",
"confirmOrder": "(de)注文を行いますか?",
"poNumberConflictError": "(de)既に同じPO Numberで注文済みもしくは発行済みの注文が存在しています。他のPO Numberで注文してください。"
"inputEmptyError": "Pflichtfeld",
"poNumberIncorrectError": "Das Format der Bestellnummer ist ungültig. Für die Bestellnummer können nur alphanumerische Zeichen eingegeben werden.",
"newOrderIncorrectError": "Bitte geben Sie für die neue Bestellung eine Zahl größer oder gleich 1 ein.",
"confirmOrder": "Möchten Sie eine Bestellung aufgeben?",
"poNumberConflictError": "Die eingegebene Bestellnummer existiert bereits. Bitte geben Sie eine andere Bestellnummer ein."
},
"label": {
"title": "(de)Order License",
"licenses": "(de)Licenses",
"poNumber": "(de)PO Number",
"newOrder": "(de)New Order",
"orderButton": "(de)Order",
"licenseTypeText": "(de)One year"
"title": "Lizenz bestellen",
"licenses": "Lizenz-Typ",
"poNumber": "Bestellnummer",
"newOrder": "Anzahl der Lizenzen",
"orderButton": "Bestellen",
"licenseTypeText": "Ein Jahr"
}
},
"dictationPage": {
"message": {
"noPlaybackAuthorization": "(de)本タスクをPlayBackできる権限がありません。",
"taskToPlaybackNoExists": "(de)タスクがすでに文字起こし完了済みまたは存在しないため、PlayBackできません。",
"taskNotEditable": "(de)すでに文字起こし作業着手中またはタスクが存在しないため、タイピストを変更できません。"
"noPlaybackAuthorization": "Sie haben keine Berechtigung zum Abspielen dieser Datei.",
"taskToPlaybackNoExists": "Die Datei kann nicht abgespielt werden, da sie bereits transkribiert wurde oder nicht existiert.",
"taskNotEditable": "Der Transkriptionist kann nicht geändert werden, da die Transkription bereits ausgeführt wird oder die Datei nicht vorhanden ist. Bitte aktualisieren Sie den Bildschirm und prüfen Sie den aktuellen Status."
},
"label": {
"title": "(de)Dictations",
"displayInfomation": "(de)Display Information",
"jobNumber": "(de)Job Number",
"status": "(de)Status",
"priority": "(de)Priority",
"encryption": "(de)Encryption",
"authorId": "(de)Author ID",
"workType": "(de)WorkType",
"fileName": "(de)File Name",
"fileLength": "(de)File Length",
"fileSize": "(de)File size",
"recordingStartedDate": "(de)Recording started date",
"recordingFinishedDate": "(de)Recording finished date",
"uploadDate": "(de)Upload date",
"transcriptionStartedDate": "(de)Transcription start date",
"transcriptionFinishedDate": "(de)Transcription finished date",
"transcriptionist": "(de)Transcriptionist",
"comment": "(de)Comment",
"optionItem1": "(de)OptionItem1",
"optionItem2": "(de)OptionItem2",
"optionItem3": "(de)OptionItem3",
"optionItem4": "(de)OptionItem4",
"optionItem5": "(de)OptionItem5",
"optionItem6": "(de)OptionItem6",
"optionItem7": "(de)OptionItem7",
"optionItem8": "(de)OptionItem8",
"optionItem9": "(de)OptionItem9",
"optionItem10": "(de)OptionItem10",
"filter": "(de)Filter",
"uploaded": "(de)Uploaded",
"inProgress": "(de)InProgress",
"finished": "(de)Finished",
"pending": "(de)Pending",
"backup": "(de)Backup",
"playback": "(de)Playback",
"fileProperty": "(de)File Property",
"changeTranscriptionist": "(de)Change Transcriptionist",
"deleteDictation": "(de)Delete Dictation",
"selectedTranscriptionist": "(de)Selected",
"poolTranscriptionist": "(de)Pool"
"title": "Diktate",
"displayInfomation": "Informations sur l'affichage",
"jobNumber": "Aufgabennummer",
"status": "Status",
"priority": "Priorität",
"encryption": "Verschlüsselung",
"authorId": "Autoren-ID",
"workType": "Aufgabentypkennung",
"fileName": "Dateiname",
"fileLength": "Dateilänge",
"fileSize": "Dateigröße",
"recordingStartedDate": "Startdatum des Diktats",
"recordingFinishedDate": "Abschlussdatum des Diktats",
"uploadDate": "Datum des Diktat-Uploads",
"transcriptionStartedDate": "Startdatum der Transkription",
"transcriptionFinishedDate": "Enddatum der Transkription",
"transcriptionist": "Transkriptionist",
"comment": "Kommentar",
"optionItem1": "Optionales Attribut 1",
"optionItem2": "Optionales Attribut 2",
"optionItem3": "Optionales Attribut 3",
"optionItem4": "Optionales Attribut 4",
"optionItem5": "Optionales Attribut 5",
"optionItem6": "Optionales Attribut 6",
"optionItem7": "Optionales Attribut 7",
"optionItem8": "Optionales Attribut 8",
"optionItem9": "Optionales Attribut 9",
"optionItem10": "Optionales Attribut 10",
"filter": "Filter",
"uploaded": "Hochgeladen",
"inProgress": "In Bearbeitung",
"finished": "Abgeschlossen",
"pending": "Ausstehend",
"backup": "Sicherungskopien",
"playback": "Wiedergabe",
"fileProperty": "Dateieigenschaft",
"changeTranscriptionist": "Transkriptionist ändern",
"deleteDictation": "Diktat löschen",
"selectedTranscriptionist": "Ausgewählter transkriptionist",
"poolTranscriptionist": "Transkriptionsliste"
}
},
"cardLicenseIssuePopupPage": {
"label": {
"title": "(de)License Card",
"subTitle": "(de)Card License Creation",
"number": "(de)Number of create licenses",
"createButton": "(de)Create"
"title": "Lizenzkarte",
"subTitle": "Erstellung von Kartenlizenzen",
"number": "Anzahl der Lizenzen",
"createButton": "Erstellen"
},
"message": {
"createSuccess": "(de)処理に成功しました。ファイルのダウンロードが完了するまでお待ちください。",
"createNumberIncorrectError": "(de)Number of create licensesには1以上の数字を入力してください。"
"createSuccess": "Die Behandlung war erfolgreich.",
"createNumberIncorrectError": "Geben Sie unter Anzahl der Lizenzen eine Zahl von 1 oder mehr ein."
}
},
"partnerLicensePage": {
"label": {
"cardLicenseButton": "(de)License Card"
"cardLicenseButton": "Lizenzkarte"
}
},
"cardLicenseActivatePopupPage": {
"label": {
"title": "(de)Activate License Key",
"keyNumber": "(de)Key number",
"activateButton": "(de)activate",
"keyNumberIncorrectError": "(de)Key Numberには20桁の半角大文字英数字を入力してください。"
"title": "Lizenzschlüssel aktivieren",
"keyNumber": "Schlüssel",
"activateButton": "Aktivieren",
"keyNumberIncorrectError": "Bitte geben Sie den 20-stelligen alphanumerischen Schlüssel ein."
},
"message": {
"LicenseKeyNotExistError": "(de)入力されたライセンスキーは存在しません。ライセンスキーを再度お確かめください。",
"LicenseKeyAlreadyActivatedError": "(de)入力されたライセンスキーは、既に有効化されています。ライセンスキーを再度お確かめください。"
"LicenseKeyNotExistError": "Der von Ihnen eingegebene Lizenzschlüssel existiert nicht. Bitte überprüfen Sie Ihren Lizenzschlüssel noch einmal.",
"LicenseKeyAlreadyActivatedError": "Der von Ihnen eingegebene Lizenzschlüssel ist bereits aktiviert. Bitte versuchen Sie es mit einem anderen Lizenzschlüssel."
}
},
"addPartnerAccountPopupPage": {
"message": {
"addAccountSuccess": "(de)メールアドレス宛に認証用メールを送信しました。",
"inputEmptyError": "(de)この項目の入力は必須です。入力してください。",
"emailIncorrectError": "(de)メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
"emailConflictError": "(de)このメールアドレスは既に登録されています。他のメールアドレスで登録してください。"
"addAccountSuccess": "Eine Bestätigungs-E-Mail wurde an Ihre E-Mail-Adresse gesendet.",
"inputEmptyError": "Pflichtfeld",
"emailIncorrectError": "Das Format der E-Mail-Adresse ist ungültig. Bitte geben Sie ein gültiges E-Mail-Adressformat ein. (Das System erkennt ungültige E-Mails, B. ohne ein -Zeichen, oder der Teil mit .com sieht nicht korrekt aus.)",
"emailConflictError": "Diese Emailadresse ist bereits registriert. Bitte registrieren Sie sich mit einer anderen E-Mail-Adresse."
},
"text": {
"title": "(de)Add account",
"accountInfoTitle": "(de)Register account information",
"countryExplanation": "(de)Please select country or the nearest country.",
"adminInfoTitle": "(de)Register primary administrator's information"
"title": "Konto hinzufügen",
"accountInfoTitle": "Kontoinformationen registrieren",
"countryExplanation": "Wählen Sie das Land aus, in dem Sie sich befinden. Wenn Ihr Land nicht aufgeführt ist, wählen Sie bitte das nächstgelegene Land aus.",
"adminInfoTitle": "Registrieren Sie die Informationen des primären Administrators"
},
"label": {
"company": "(de)Company Name",
"country": "(de)Country",
"adminName": "(de)Admin Name",
"email": "(de)Email",
"createAccountButton": "(de)Add account"
"company": "Name der Firma",
"country": "Land",
"adminName": "Name des Administrators",
"email": "Email",
"createAccountButton": "Konto hinzufügen"
}
},
"partnerLicense": {
"label": {
"title": "(de)License",
"subTitle": "(de)License for partners",
"orderLicenseButton": "(de)Order License",
"orderHistoryButton": "(de)Order History",
"IssueLicenseCardButton": "(de)License Card",
"displayCardHistoryButton": "(de)Card History",
"name": "(de)Name",
"category": "(de)Category",
"accountId": "(de)Account ID",
"stockLicense": "(de)Stock license",
"issueRequested": "(de)Issue Requested",
"shortage": "(de)Shortage",
"issueRequesting": "(de)Issue Requesting",
"viewDetails": "(de)View details",
"accounts": "(de)accounts"
"title": "Lizenz",
"subTitle": "Partnerlizenzen",
"orderLicenseButton": "Lizenz bestellen",
"orderHistoryButton": "Bestellverlauf",
"IssueLicenseCardButton": "Lizenzkarte",
"displayCardHistoryButton": "Kartenverlauf",
"name": "Name der Firma",
"category": "Kontoebene",
"accountId": "Autoren-ID",
"stockLicense": "Lizenzinventar",
"issueRequested": "Lizenzen angefordert",
"shortage": "Lizenzmangel",
"issueRequesting": "Lizenzen auf Bestellung",
"viewDetails": "Details anzeigen",
"accounts": "konten"
}
},
"orderHistoriesPage": {
"label": {
"title": "(de)License",
"orderHistory": "(de)Order History",
"orderDate": "(de)Order date",
"issueDate": "(de)Issue date",
"numberOfOrder": "(de)Number of Order",
"poNumber": "(de)PO Number",
"status": "(de)Status",
"issueRequesting": "(de)Issue Requesting",
"issued": "(de)Issued",
"orderCanceled": "(de)Order Canceled",
"issue": "(de)Issue",
"issueCancel": "(de)Issue Cancel",
"orderCancel": "(de)Order Cancel",
"histories": "(de)histories"
"title": "Lizenz",
"orderHistory": "Bestellverlauf",
"orderDate": "Auftragsdatum",
"issueDate": "Ausgabetag",
"numberOfOrder": "Anzahl der bestellten Lizenzen",
"poNumber": "Bestellnummer",
"status": "Status",
"issueRequesting": "Lizenzen auf Bestellung",
"issued": "Lizenz ausgestellt",
"orderCanceled": "Bestellung storniert",
"issue": "Ausgabe",
"issueCancel": "Lizenzen kündigen",
"orderCancel": "Bestellung stornieren",
"histories": "geschichten"
},
"message": {
"notEnoughOfNumberOfLicense": "(de)ライセンスが不足しているため、発行することができませんでした。ライセンスの注文を行ってください。",
"alreadyIssueLicense": "(de)すでに発行済みの注文です。画面を更新してください。",
"alreadyLicenseIssueOrCancel": "(de)ライセンス注文のキャンセルに失敗しました。選択された注文はすでに発行またはキャンセルされています。画面を更新して再度ご確認ください。",
"alreadyLicenseStatusChanged": "(de)ライセンス発行のキャンセルに失敗しました。選択された注文の状態が変更されています。画面を更新して再度ご確認ください。",
"expiredSinceIssued": "(de)発行日から一定期間経過しているため、発行キャンセルできません。",
"alreadyLicenseAllocated": "(de)発行したライセンスがすでに割り当てられたため、発行キャンセルできません。"
"notEnoughOfNumberOfLicense": "Lizenzen konnten aufgrund unzureichender Lizenzanzahl nicht ausgestellt werden. Bitte bestellen Sie zusätzliche Lizenzen.",
"alreadyIssueLicense": "Die Bestellung wurde bereits aufgegeben. Die angezeigten Informationen sind möglicherweise veraltet. Aktualisieren Sie daher bitte den Bildschirm, um den neuesten Status anzuzeigen.",
"alreadyLicenseIssueOrCancel": "Lizenzbestellung konnte nicht storniert werden. Die Lizenz für die ausgewählte Bestellung wurde bereits ausgestellt oder die Bestellung wurde bereits storniert. Bitte aktualisieren Sie den Bildschirm und überprüfen Sie ihn erneut.",
"alreadyLicenseStatusChanged": "Die Lizenzausstellung konnte nicht storniert werden. Der Status der ausgewählten Bestellung hat sich geändert. Bitte aktualisieren Sie den Bildschirm und überprüfen Sie ihn erneut.",
"expiredSinceIssued": "Ausgestellte Lizenzen können nicht storniert werden, da seit dem Ausstellungsdatum mehr als 14 Tage vergangen sind.",
"alreadyLicenseAllocated": "Die ausgestellte Lizenz kann nicht storniert werden, da sie bereits zugewiesen wurde."
}
},
"allocateLicensePopupPage": {
"label": {
"title": "(de)License Allocation",
"personalInformation": "(de)Personal Information",
"email": "(de)Email",
"name": "(de)Name",
"authorID": "(de)Author ID",
"status": "(de)Status",
"allocated": "(de)Allocated",
"notAllocated": "(de)Not Allocated",
"expirationDate": "(de)Expiration date",
"licenseInformation": "(de)License Information",
"licenseAvailable": "(de)License available",
"dropDownHeading": "(de)Select a license.",
"oneYear": "(de)One Year",
"allocateLicense": "(de)OK"
"title": "Lizenz zuweisen",
"email": "Email",
"name": "Name",
"authorID": "Autoren-ID",
"status": "Status",
"allocated": "Lizenz zugewiesen",
"notAllocated": "Lizenz nicht zugewiesen",
"expirationDate": "Verfallsdatum",
"licenseInformation": "Lizenzinformationen",
"licenseAvailable": "Verfügbare Lizenz",
"dropDownHeading": "Lizenz auswählen",
"oneYear": "Ein Jahr",
"allocateLicense": "OK"
},
"message": {
"inputEmptyError": "(de)この項目の入力は必須です。入力してください。",
"licenseAllocationFailure": "(de)ライセンスの割り当てに失敗しました。他のライセンスを選択して再度割り当てをしてください。"
"inputEmptyError": "Pflichtfeld",
"licenseAllocationFailure": "Lizenz konnte nicht zugewiesen werden. Die angezeigten Informationen sind möglicherweise veraltet. Aktualisieren Sie daher bitte den Bildschirm, um den neuesten Status anzuzeigen."
}
},
"workflowPage": {
"label": {
"title": "(de)Workflow"
"title": "Arbeitsablauf"
}
},
"typistGroupSetting": {
"label": {
"title": "(de)Transctiprionist Group",
"addGroup": "(de)Add Group",
"groupName": "(de)Group Name",
"addTypistGroup": "(de)Add Transcriptionist Group",
"transcriptionist": "(de)Transcriptionist",
"selected": "(de)Selected",
"pool": "(de)Pool",
"add": "(de)Add",
"remove": "(de)Remove",
"editTypistGroup": "(de)Edit Transcriptionist Group"
"title": "Transkriptionistengruppe",
"addGroup": "Gruppe hinzufügen",
"groupName": "Gruppenname",
"addTypistGroup": "Transkriptionist Gruppe hinzufügen",
"transcriptionist": "Transkriptionist",
"selected": "Ausgewählter transkriptionist",
"pool": "Transkriptionsliste",
"add": "Hinzufügen",
"remove": "Entfernen",
"editTypistGroup": "Transkriptionistengruppe bearbeiten"
},
"message": {
"selectedTypistEmptyError": "(de)TranscriptionistがいないTranscriptionistGroupは保存できません。1名以上をTranscriptionistとして選択してください。",
"groupSaveFailedError": "(de)TypistGroupの保存に失敗しました。画面を更新し、再度実行してください"
"selectedTypistEmptyError": "Um eine Transkriptionsgruppe zu speichern, müssen ein oder mehrere Transkriptionisten ausgewählt werden.",
"groupSaveFailedError": "Die Schreibkraftgruppe konnte nicht gespeichert werden. Die angezeigten Informationen sind möglicherweise veraltet. Aktualisieren Sie daher bitte den Bildschirm, um den neuesten Status anzuzeigen."
}
},
"worktypeIdSetting": {
"label": {
"title": "(de)Worktype ID Setting",
"activeWorktypeId": "(de)Active Worktype ID",
"addWorktypeId": "(de)Add Worktype ID",
"worktypeId": "(de)Worktype ID",
"description": "(de)Description",
"descriptionOptional": "(de)Description (Optional)",
"optionItem": "(de)Option Item",
"worktypeIdTerms": "(de)WorktypeID should be alphanumeric and symbols,\nbut not include: \\ / : * ? “ < > | .",
"addWorktype": "(de)Add Worktype",
"editWorktypeId": "(de)Edit Worktype ID",
"saveChange": "(de)Save Changes",
"title": "Einstellung der Aufgabentypkennung",
"activeWorktypeId": "Aktive Aufgabentypkennung",
"addWorktypeId": "Aufgabentypkennung hinzufügen",
"worktypeId": "Aufgabentypkennung",
"description": "Beschreibung",
"descriptionOptional": "Beschreibung (Optional)",
"optionItem": "Optionales Attribut",
"worktypeIdTerms": "Die Aufgabentypkennung kann alphanumerische Zeichen und Symbole enthalten. Die folgenden Symbole können nicht verwendet werden:\\/:*?\"<>|.",
"addWorktype": "Aufgabentyp hinzufügen",
"editWorktypeId": "Aufgabentypkennung bearbeiten",
"saveChange": "Änderungen speichern",
"editOptionItems": "(de)Option Item",
"itemLabel": "(de)Item label",
"defaultValue": "(de)Default value",
@ -404,28 +400,40 @@
"optionItemTerms": "(de)The Item label and Initial value should be alphanumeric and symbols, but not include: \\ / : * ? “ < > | ."
},
"message": {
"worktypeIdIncorrectError": "(de)入力されたWorktypeIDがルールを満たしていません。下記のルールを満たすWorktypeIDを入力してください",
"alreadyWorktypeIdExistError": "(de)このWorktype IDは既に登録されています。他のWorktype IDで登録してください。",
"worktypeIDLimitError": "(de)Worktype IDが登録件数の上限に達しているため追加できません。",
"worktypeIdIncorrectError": "Die von Ihnen eingegebene Aufgabentypkennung entspricht nicht den Spezifikationen. Bitte geben Sie den korrekten Arbeitstyp ein, wie in den Spezifikationen unten beschrieben.",
"alreadyWorktypeIdExistError": "Diese Aufgabentypkennung ist derzeit registriert. Bitte registrieren Sie sich mit einer anderen Worktype-ID.",
"worktypeIDLimitError": "Die Aufgabentypkennung kann nicht hinzugefügt werden, da die maximale Anzahl an Registrierungen erreicht wurde.",
"optionItemInvalidError": "(de)Default valueがDefaultに設定されている場合、Initial valueは入力が必須です。",
"optionItemSaveFailedError": "(de)オプションアイテムの保存に失敗しました。画面を更新し、再度実行してください",
"optionItemIncorrectError": "(de)入力されたItem labelまたはInitial valueがルールを満たしていません。下記のルールを満たす値を入力してください",
"updateActiveWorktypeFailedError": "(de)Active WorktypeIDの保存に失敗しました。画面を更新し、再度実行してください"
}
},
"templateFilePage": {
"label": {
"title": "(de)Template List",
"addTemplate": "(de)Add Template",
"fileName": "(de)Flie Name",
"chooseFile": "(de)Choose File",
"notFileChosen": "(de)- Not file chosen -",
"fileSizeTerms": "(de)ファイルサイズは5MBまでです。",
"fileSizeError": "(de)選択されたファイルのサイズが大きすぎます。サイズが5MB以下のファイルを選択してください。",
"fileEmptyError": "(de)ファイル選択は必須です。ファイルを選択してください。"
}
},
"partnerPage": {
"label": {
"title": "(de)Partners",
"addAccount": "(de)Add Account",
"name": "(de)Name",
"category": "(de)Category",
"accountId": "(de)Account ID",
"country": "(de)Country",
"primaryAdmin": "(de)Primary Admin",
"email": "(de)E-mail",
"dealerManagement": "(de)Dealer Management",
"partners": "(de)partners",
"deleteAccount": "(de)Delete Account"
"title": "Partner",
"addAccount": "Konto hinzufügen",
"name": "Name der Firma",
"category": "Kontoebene",
"accountId": "Autoren-ID",
"country": "Land",
"primaryAdmin": "Hauptadministrator",
"email": "Email",
"dealerManagement": "Erlauben Sie dem Händler, Änderungen vorzunehmen",
"partners": "Partner",
"deleteAccount": "Konto löschen"
}
},
"accountPage": {
@ -451,5 +459,14 @@
"message": {
"updateAccountFailedError": "(de)アカウント情報の保存に失敗しました。画面を更新し、再度実行してください"
}
},
"deleteAccountPopup": {
"label": {
"title": "(de)Delete Account",
"subTitle": "(de)Delete your account",
"cautionOfDeleteingAccountData": "(de)Deleting your account will remove all of your audio files and\nlicenses from system. and you'll cannot use ODMS Cloud.\nThis cannot be undone.",
"deleteButton": "(de)Delete account",
"cancelButton": "(de)Cancel"
}
}
}

View File

@ -1,19 +1,17 @@
{
"common": {
"message": {
"inputEmptyError": "この項目の入力は必須です。入力してください。",
"passwordIncorrectError": "Error Message",
"emailIncorrectError": "Error Message",
"internalServerError": "処理に失敗しました。時間をおいて再実行しても解決しない場合はシステム管理者にお問い合わせください。",
"permissionDeniedError": "操作を実行する権限がありません。",
"listEmpty": "検索結果が0件です。",
"dialogConfirm": "操作を実行しますか?",
"success": "処理に成功しました。"
"inputEmptyError": "Mandatory Field",
"passwordIncorrectError": "入力されたパスワードがルールを満たしていません。下記のルールを満たすパスワードを入力してください。",
"emailIncorrectError": "メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
"internalServerError": "Processing failed. Please try again later. ",
"listEmpty": "There are 0 search results.",
"dialogConfirm": "Do you want to perform the operation?",
"success": "Successfully Processed"
},
"label": {
"cancel": "Cancel",
"headerTitle": "ODMS Cloud",
"copyRight": "OM Digital Solutions 2023",
"copyRight": "© OM Digital Solutions Corporation",
"edit": "Edit",
"save": "Save",
"delete": "Delete",
@ -22,181 +20,180 @@
"tier2": "BC",
"tier3": "Distributor",
"tier4": "Dealer",
"tier5": "Customer"
"tier5": "Customer",
"notSelected": "None"
}
},
"topPage": {
"message": {
"logout": "ログイン有効期限が切れました。再度ログイン手続きを行ってください。"
"logout": "Your login has expired. Please login again."
},
"label": {
"displayLanguage": "Display language",
"displayLanguage": "Language",
"languageEnglish": "English",
"languageGerman": "German",
"languageSpanish": "Spanish",
"languageFrench": "French",
"alreadyHaveAccount": "Already have an account?",
"languageGerman": "Deutsch",
"languageSpanish": "Español",
"languageFrench": "Français",
"alreadyHaveAccount": "Existing User",
"signInButton": "Sign in",
"newUser": "New user?",
"signUpButton": "Create a new account",
"newUser": "New User",
"signUpButton": "Create Account",
"logoAlt": "OM Dictation Management System in the Cloud"
}
},
"signupPage": {
"message": {
"inputEmptyError": "この項目の入力は必須です。入力してください。",
"passwordIncorrectError": "入力されたパスワードがルールを満たしていません。下記のルールを満たすパスワードを入力してください。",
"emailIncorrectError": "メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
"dealerNotFoundError": "指定されたディーラーが見つかりませんでした。直接ディーラーを指定してください。"
"inputEmptyError": "Mandatory Field",
"passwordIncorrectError": "The password you entered does not meet the rules. Please enter a password that meets the rules below.",
"emailIncorrectError": "E-mail address format is invalid. Please enter a valid email address format. (System identifies invalid emails such as without an mark or the part with .com is something that doesn't look correct. )",
"dealerNotFoundError": "There is a incorrect value in the dealer selection. Please select a dealer again."
},
"text": {
"title": "Create your account",
"pageExplanation": "Explanation...",
"accountInfoTitle": "Register your account information",
"countryExplanation": " Please select your country or the nearest country.",
"dealerExplanation": "Please select the dealer to purchase a license.",
"accountInfoTitle": "Registration Information",
"countryExplanation": "Select the country where you are located. If your country isn't listed, please select the nearest country.",
"dealerExplanation": "Please select the dealer you would like to purchase the license from.",
"adminInfoTitle": "Register primary administrator's information",
"passwordTerms": "Please set a password or issue an initial password.\nThe password must be more than 8 or less than 64 letters,numbers and symbols."
"passwordTerms": "Please set a password. The password must be 8-25 characters must contain letters, numbers, and symbols. (Should list compatible symbol and state if capital letter is needed)."
},
"label": {
"company": "Company Name",
"country": "Country",
"dealer": "Dealer",
"adminName": "Admin Name",
"email": "Email",
"dealer": "Dealer (Optional)",
"adminName": "Administrators Name",
"email": "Email Address",
"password": "Password",
"termsLink": "Click here to read the terms of use",
"termsLinkFor": "for ODDS.",
"termsCheckBox": "Yes, I agree to the terms of use.",
"createAccountButton": "Create account"
"createAccountButton": "Submit"
}
},
"signupConfirmPage": {
"message": {
"emailConflictError": "このメールアドレスは既に登録されています。他のメールアドレスで登録してください。"
"emailConflictError": "This email address is already registered. Please register with another email address."
},
"text": {
"title": "Confirmation",
"pageExplanation": "Explanation ......",
"title": "Verification ",
"pageExplanation": "Please verify that the below information is correct. Then click Register.",
"accountInfoTitle": "Your account information",
"adminInfoTitle": "Primary administrator's information"
},
"label": {
"company": "Company Name",
"country": "Country",
"dealer": "Dealer (Optional)",
"adminName": "Admin Name",
"email": "Email",
"dealer": "Dealer (Optional)",
"adminName": "Administrators Name",
"email": "Email Address",
"password": "Password",
"signupButton": "Sign up"
"signupButton": "Register"
}
},
"signupCompletePage": {
"text": {
"createdInfo": "Your account has been created and a verification email has been set to your registered email address. Please click on the verification link included in the email to activate your account."
"createdInfo": "Your account has been created and a verification email has been sent to your registered email address. Please click the verification link in the email to activate your account."
},
"label": {
"title": "Account created"
"title": "Account Created"
}
},
"signupVerifyPage": {
"text": {
"verifySuccess": "You have successfully verified the account.",
"faild": "The verification url does not match. Please try again,\nor click on the link below to receive a new verification mail.",
"verifySuccess": "Your account has been successfully verified.",
"faild": "Verification failed. Please click the below link to resend confirmation email.",
"alreadySuccess": "Your account has already been verified."
},
"label": {
"verifySuccess": "Verified!",
"faild": "Verification failed",
"faild": "Verification Failed",
"alreadySuccess": "Already Verified!",
"returnToSignIn": "Return to Sign in"
"returnToSignIn": "Click to Sign in"
}
},
"userListPage": {
"message": {
"addUserSuccess": "メールアドレス宛に認証用メールを送信しました。",
"authorIdConflictError": "このAuthor IDは既に登録されています。他のAuthor IDで登録してください。",
"authorIdIncorrectError": "Author IDの形式が不正です。Author IDは半角英数字(大文字)と\"_\"のみ入力可能です。",
"roleChangeError": "Roleの変更に失敗しました。画面を更新して再度ユーザー情報を取得してください。",
"encryptionPasswordCorrectError": "EncryptionPasswordがルールを満たしていません。",
"alreadyLicenseDeallocatedError": "すでにライセンス割り当てが解除されています。画面を更新して再度ご確認下さい。"
"addUserSuccess": "A verification email has been sent to your email address.",
"authorIdConflictError": "This Author ID has already been registered. Please register with another Author ID.",
"authorIdIncorrectError": "Author ID format is invalid. Only alphanumeric characters and \"_\" can be entered for Author ID.",
"roleChangeError": "Unable to change the User Role. The displayed information may be outdated, so please refresh the screen to see the latest status.",
"encryptionPasswordCorrectError": "Encryption password does not meet the rules.",
"alreadyLicenseDeallocatedError": "Assigned license has already been canceled. The displayed information may be outdated, so please refresh the screen to see the latest status."
},
"label": {
"title": "User",
"addUser": "Add User",
"licenseAllocation": "License Allocation",
"licenseAllocation": "Assign License",
"name": "Name",
"role": "Role",
"authorID": "Author ID",
"typistGroup": "Typist Group",
"typistGroup": "Transcriptionist Group",
"email": "Email",
"status": "Status",
"expiration": "Expiration",
"remaining": "Remaining",
"autoRenew": "Auto renew",
"licenseAlert": "License alert",
"expiration": "Expiration Date",
"remaining": "Remaining Period",
"autoRenew": "Auto Renew",
"licenseAlert": "License Alert",
"notification": "Notification",
"users": "users",
"users": "Users",
"of": "of",
"personal": "Personal information",
"setting": "Setting",
"selectGroup": "Select group",
"addToGroup": "Add to group (Optional)",
"personal": "User Information",
"setting": "Settings",
"selectGroup": "Select Group",
"addToGroup": "Add Group (Optional)",
"author": "Author",
"transcriptionist": "Transcriptionist",
"encryption": "Encryption",
"prompt": "Prompt",
"emailVerified": "Email Verified",
"editUser": "Edit User",
"licenseDeallocation": "License Deallocation",
"licenseDeallocation": "Unassign License",
"deleteUser": "Delete User",
"none": "None",
"encryptionPassword": "Password:",
"encryptionPasswordTerm": "Please set your password using 4 to 16 ASCII characters.\nYour password may include letters, numbers, and symbols."
"encryptionPassword": "Password",
"encryptionPasswordTerm": "Please set your password using 4 to 16 alphanumeric and symbols."
}
},
"LicenseSummaryPage": {
"label": {
"title": "License",
"subTitle": "EFGI Legal",
"title": "License(s)",
"orderLicense": "Order License",
"orderHistory": "Order History",
"activateLicenseKey": "Activate License Key",
"totalLicense": "Total license",
"allocatedLicense": "Allocated license",
"reusableLicense": "Reusable license",
"freeLicense": "Free license",
"expiringWithin14daysLicense": "Expiring within 14days license",
"issueRequesting": "Issue Requesting",
"numberOfRequesting": "Number of Requesting",
"totalLicense": "License Inventory",
"allocatedLicense": "Number of licenses allocated ",
"reusableLicense": "Number of licenses available for reuse",
"freeLicense": "Number of unused licenses",
"expiringWithin14daysLicense": "Number of licenses expiring within 14 days",
"issueRequesting": "Total number of licenses on order",
"numberOfRequesting": "Total number of orders",
"shortage": "Shortage",
"storageSize": "Storage Size",
"usedSize": "Used Size",
"storageAvailable": "Storage Available"
"storageSize": "Storage Available",
"usedSize": "Storage Used",
"storageAvailable": "Storage Unavailable (Exceeded Amount)"
}
},
"licenseOrderPage": {
"message": {
"inputEmptyError": "この項目の入力は必須です。入力してください。",
"poNumberIncorrectError": "PO Numberの形式が不正です。PO Numberは半角英数字(大文字)のみ入力可能です。",
"newOrderIncorrectError": "New Orderには1以上の数字を入力してください。",
"confirmOrder": "注文を行いますか?",
"poNumberConflictError": "既に同じPO Numberで注文済みもしくは発行済みの注文が存在しています。他のPO Numberで注文してください。"
"inputEmptyError": "Mandatory Field",
"poNumberIncorrectError": "PO Number format is not valid. Only alphanumeric characters can be entered for the PO Number.",
"newOrderIncorrectError": "Please enter a number greater than or equal to 1 for the New Order.",
"confirmOrder": "Would you like to place an order?",
"poNumberConflictError": "PO Number entered already exists. Please enter a different PO Number."
},
"label": {
"title": "Order License",
"licenses": "Licenses",
"licenses": "License Type",
"poNumber": "PO Number",
"newOrder": "New Order",
"newOrder": "Number of licenses",
"orderButton": "Order",
"licenseTypeText": "One year"
}
},
"dictationPage": {
"message": {
"noPlaybackAuthorization": "本タスクをPlayBackできる権限がありません。",
"taskToPlaybackNoExists": "タスクがすでに文字起こし完了済みまたは存在しないため、PlayBackできません。",
"taskNotEditable": "すでに文字起こし作業着手中またはタスクが存在しないため、タイピストを変更できません。"
"noPlaybackAuthorization": "You do not have permission to playback this file.",
"taskToPlaybackNoExists": "The file cannot be played because it has already been transcribed or does not exist.",
"taskNotEditable": "The transcriptionist cannot be changed because the transcription is already in progress or the file does not exist. Please refresh the screen and check the latest status."
},
"label": {
"title": "Dictations",
@ -206,15 +203,15 @@
"priority": "Priority",
"encryption": "Encryption",
"authorId": "Author ID",
"workType": "WorkType",
"workType": "Worktype ID",
"fileName": "File Name",
"fileLength": "File Length",
"fileSize": "File size",
"recordingStartedDate": "Recording started date",
"recordingFinishedDate": "Recording finished date",
"uploadDate": "Upload date",
"recordingStartedDate": "Dictation start date",
"recordingFinishedDate": "Dictation complete date",
"uploadDate": "Dictation upload date",
"transcriptionStartedDate": "Transcription start date",
"transcriptionFinishedDate": "Transcription finished date",
"transcriptionFinishedDate": "Transcription finish date",
"transcriptionist": "Transcriptionist",
"comment": "Comment",
"optionItem1": "OptionItem1",
@ -237,8 +234,8 @@
"fileProperty": "File Property",
"changeTranscriptionist": "Change Transcriptionist",
"deleteDictation": "Delete Dictation",
"selectedTranscriptionist": "Selected",
"poolTranscriptionist": "Pool"
"selectedTranscriptionist": "Selected Transcriptionist",
"poolTranscriptionist": "Transcription List"
}
},
"cardLicenseIssuePopupPage": {
@ -249,8 +246,8 @@
"createButton": "Create"
},
"message": {
"createSuccess": "処理に成功しました。ファイルのダウンロードが完了するまでお待ちください。",
"createNumberIncorrectError": "Number of create licensesには1以上の数字を入力してください。"
"createSuccess": "Processing was successful.",
"createNumberIncorrectError": "Enter a number of 1 or more in Number of licenses."
}
},
"partnerLicensePage": {
@ -261,32 +258,32 @@
"cardLicenseActivatePopupPage": {
"label": {
"title": "Activate License Key",
"keyNumber": "Key number",
"activateButton": "activate",
"keyNumberIncorrectError": "Key Numberには20桁の半角大文字英数字を入力してください。"
"keyNumber": "Key",
"activateButton": "Activate",
"keyNumberIncorrectError": "Please enter the 20 character alphanumeric key."
},
"message": {
"LicenseKeyNotExistError": "入力されたライセンスキーは存在しません。ライセンスキーを再度お確かめください。",
"LicenseKeyAlreadyActivatedError": "入力されたライセンスキーは、既に有効化されています。ライセンスキーを再度お確かめください。"
"LicenseKeyNotExistError": "The license key you entered does not exist. Please check your license key again.",
"LicenseKeyAlreadyActivatedError": "The license key you entered is already activated. Please try using a different license key."
}
},
"addPartnerAccountPopupPage": {
"message": {
"addAccountSuccess": "メールアドレス宛に認証用メールを送信しました。",
"inputEmptyError": "この項目の入力は必須です。入力してください。",
"emailIncorrectError": "メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
"emailConflictError": "このメールアドレスは既に登録されています。他のメールアドレスで登録してください。"
"addAccountSuccess": "A verification email has been sent to your email address.",
"inputEmptyError": "Mandatory field",
"emailIncorrectError": "E-mail address format is invalid. Please enter a valid email address format. (System identifies invalid emails such as without an mark or the part with .com is something that doesn't look correct. )",
"emailConflictError": "This email address is already registered. Please register with another email address."
},
"text": {
"title": "Add account",
"accountInfoTitle": "Register account information",
"countryExplanation": "Please select country or the nearest country.",
"countryExplanation": "Select the country where you are located. If your country isn't listed, please select the nearest country.",
"adminInfoTitle": "Register primary administrator's information"
},
"label": {
"company": "Company Name",
"country": "Country",
"adminName": "Admin Name",
"adminName": "Administrators Name",
"email": "Email",
"createAccountButton": "Add account"
}
@ -294,19 +291,19 @@
"partnerLicense": {
"label": {
"title": "License",
"subTitle": "License for partners",
"subTitle": "Partner Licenses",
"orderLicenseButton": "Order License",
"orderHistoryButton": "Order History",
"IssueLicenseCardButton": "License Card",
"displayCardHistoryButton": "Card History",
"name": "Name",
"category": "Category",
"name": "Company Name",
"category": "Account Level",
"accountId": "Account ID",
"stockLicense": "Stock license",
"issueRequested": "Issue Requested",
"shortage": "Shortage",
"issueRequesting": "Issue Requesting",
"viewDetails": "View details",
"stockLicense": "License Inventory",
"issueRequested": "Licenses Requested",
"shortage": "License Shortage",
"issueRequesting": "Licenses on Order",
"viewDetails": "View Details",
"accounts": "accounts"
}
},
@ -314,48 +311,47 @@
"label": {
"title": "License",
"orderHistory": "Order History",
"orderDate": "Order date",
"issueDate": "Issue date",
"numberOfOrder": "Number of Order",
"orderDate": "Order Date",
"issueDate": "Issue Date",
"numberOfOrder": "Number of licenses on order",
"poNumber": "PO Number",
"status": "Status",
"issueRequesting": "Issue Requesting",
"issued": "Issued",
"issueRequesting": "Licenses on Order",
"issued": "License Issued",
"orderCanceled": "Order Canceled",
"issue": "Issue",
"issueCancel": "Issue Cancel",
"orderCancel": "Order Cancel",
"issueCancel": "Cancel Licenses",
"orderCancel": "Cancel Order",
"histories": "histories"
},
"message": {
"notEnoughOfNumberOfLicense": "ライセンスが不足しているため、発行することができませんでした。ライセンスの注文を行ってください。",
"alreadyIssueLicense": "すでに発行済みの注文です。画面を更新してください。",
"alreadyLicenseIssueOrCancel": "ライセンス注文のキャンセルに失敗しました。選択された注文はすでに発行またはキャンセルされています。画面を更新して再度ご確認ください。",
"alreadyLicenseStatusChanged": "ライセンス発行のキャンセルに失敗しました。選択された注文の状態が変更されています。画面を更新して再度ご確認ください。",
"expiredSinceIssued": "発行日から一定期間経過しているため、発行キャンセルできません。",
"alreadyLicenseAllocated": "発行したライセンスがすでに割り当てられたため、発行キャンセルできません。"
"notEnoughOfNumberOfLicense": "Licenses could not be issued due to insufficient amount of licenses. Please order additional licenses.",
"alreadyIssueLicense": "The order has already been placed. The displayed information may be outdated, so please refresh the screen to see the latest status.",
"alreadyLicenseIssueOrCancel": "Failed to cancel license order. The license for the selected order has already been issued or the order has been canceled already. Please refresh the screen and check again.",
"alreadyLicenseStatusChanged": "Failed to cancel license issuance. The status of the selected order has changed. Please refresh the screen and check again.",
"expiredSinceIssued": "Licenses that have been issued cannot be canceled because more than 14 days have passed since the issue date.",
"alreadyLicenseAllocated": "The issued license cannot be canceled because it has already been assigned."
}
},
"allocateLicensePopupPage": {
"label": {
"title": "License Allocation",
"personalInformation": "Personal Information",
"title": "Assign License",
"email": "Email",
"name": "Name",
"authorID": "Author ID",
"status": "Status",
"allocated": "Allocated",
"notAllocated": "Not Allocated",
"expirationDate": "Expiration date",
"allocated": "License Assigned",
"notAllocated": "License Not Assigned",
"expirationDate": "Expiration Date",
"licenseInformation": "License Information",
"licenseAvailable": "License available",
"dropDownHeading": "Select a license.",
"oneYear": "One Year",
"licenseAvailable": "Available License",
"dropDownHeading": "Select License",
"oneYear": "One year",
"allocateLicense": "OK"
},
"message": {
"inputEmptyError": "この項目の入力は必須です。入力してください。",
"licenseAllocationFailure": "ライセンスの割り当てに失敗しました。他のライセンスを選択して再度割り当てをしてください。"
"inputEmptyError": "Mandatory Field",
"licenseAllocationFailure": "Failed to assign license. The displayed information may be outdated, so please refresh the screen to see the latest status."
}
},
"workflowPage": {
@ -365,20 +361,20 @@
},
"typistGroupSetting": {
"label": {
"title": "Transctiprionist Group",
"title": "Transcriptionist Group",
"addGroup": "Add Group",
"groupName": "Group Name",
"addTypistGroup": "Add Transcriptionist Group",
"transcriptionist": "Transcriptionist",
"selected": "Selected",
"pool": "Pool",
"selected": "Selected Transcriptionist",
"pool": "Transcription List",
"add": "Add",
"remove": "Remove",
"editTypistGroup": "Edit Transcriptionist Group"
},
"message": {
"selectedTypistEmptyError": "TranscriptionistがいないTranscriptionistGroupは保存できません。1名以上をTranscriptionistとして選択してください。",
"groupSaveFailedError": "TypistGroupの保存に失敗しました。画面を更新し、再度実行してください"
"selectedTypistEmptyError": "One or more transcriptonist must be selected to save a transcrption group.",
"groupSaveFailedError": "Typist Group could not be saved. The displayed information may be outdated, so please refresh the screen to see the latest status."
}
},
"worktypeIdSetting": {
@ -390,7 +386,7 @@
"description": "Description",
"descriptionOptional": "Description (Optional)",
"optionItem": "Option Item",
"worktypeIdTerms": "WorktypeID should be alphanumeric and symbols,\nbut not include: \\ / : * ? “ < > | .",
"worktypeIdTerms": "Worktype ID can contain alphanumeric and symbols. The following symbols cannot be used:\\/:*?\"<>|.",
"addWorktype": "Add Worktype",
"editWorktypeId": "Edit Worktype ID",
"saveChange": "Save Changes",
@ -404,27 +400,39 @@
"optionItemTerms": "The Item label and Initial value should be alphanumeric and symbols, but not include: \\ / : * ? “ < > | ."
},
"message": {
"worktypeIdIncorrectError": "入力されたWorktypeIDがルールを満たしていません。下記のルールを満たすWorktypeIDを入力してください",
"alreadyWorktypeIdExistError": "このWorktype IDは既に登録されています。他のWorktype IDで登録してください。",
"worktypeIDLimitError": "Worktype IDが登録件数の上限に達しているため追加できません。",
"worktypeIdIncorrectError": "The Worktype ID you entered does not meet specifications. Please enter the correct Worktype as outlined in the specifications below.",
"alreadyWorktypeIdExistError": "This Worktype ID is currently registered. Please register with a different Worktype ID.",
"worktypeIDLimitError": "Worktype ID cannot be added because it has reached the maximum number of registrations.",
"optionItemInvalidError": "Default valueがDefaultに設定されている場合、Initial valueは入力が必須です。",
"optionItemSaveFailedError": "オプションアイテムの保存に失敗しました。画面を更新し、再度実行してください",
"optionItemIncorrectError": "入力されたItem labelまたはInitial valueがルールを満たしていません。下記のルールを満たす値を入力してください",
"updateActiveWorktypeFailedError": "Active WorktypeIDの保存に失敗しました。画面を更新し、再度実行してください"
}
},
"templateFilePage": {
"label": {
"title": "Template List",
"addTemplate": "Add Template",
"fileName": "Flie Name",
"chooseFile": "Choose File",
"notFileChosen": "- Not file chosen -",
"fileSizeTerms": "ファイルサイズは5MBまでです。",
"fileSizeError": "選択されたファイルのサイズが大きすぎます。サイズが5MB以下のファイルを選択してください。",
"fileEmptyError": "ファイル選択は必須です。ファイルを選択してください。"
}
},
"partnerPage": {
"label": {
"title": "Partners",
"addAccount": "Add Account",
"name": "Name",
"category": "Category",
"addAccount": "Add account",
"name": "Company Name",
"category": "Account Level",
"accountId": "Account ID",
"country": "Country",
"primaryAdmin": "Primary Admin",
"email": "E-mail",
"primaryAdmin": "Primary administrator",
"email": "Email",
"dealerManagement": "Dealer Management",
"partners": "partners",
"partners": "Partners",
"deleteAccount": "Delete Account"
}
},
@ -451,5 +459,14 @@
"message": {
"updateAccountFailedError": "アカウント情報の保存に失敗しました。画面を更新し、再度実行してください"
}
},
"deleteAccountPopup": {
"label": {
"title": "Delete Account",
"subTitle": "Delete your account",
"cautionOfDeleteingAccountData": "Deleting your account will remove all of your audio files and\nlicenses from system. and you'll cannot use ODMS Cloud.\nThis cannot be undone.",
"deleteButton": "Delete account",
"cancelButton": "Cancel"
}
}
}

View File

@ -1,399 +1,395 @@
{
"common": {
"message": {
"inputEmptyError": "(es)この項目の入力は必須です。入力してください。",
"passwordIncorrectError": "(es)Error Message",
"emailIncorrectError": "(es)Error Message",
"internalServerError": "(es)処理に失敗しました。時間をおいて再実行しても解決しない場合はシステム管理者にお問い合わせください。",
"permissionDeniedError": "(es)操作を実行する権限がありません。",
"listEmpty": "(es)検索結果が0件です。",
"dialogConfirm": "(es)操作を実行しますか?",
"success": "(es)処理に成功しました。"
"inputEmptyError": "Campo obligatorio",
"passwordIncorrectError": "(es)入力されたパスワードがルールを満たしていません。下記のルールを満たすパスワードを入力してください。",
"emailIncorrectError": "(es)メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
"internalServerError": "El procesamiento falló. Por favor, inténtelo de nuevo más tarde.",
"listEmpty": "Hay 0 resultados de búsqueda.",
"dialogConfirm": "¿Quieres realizar la operación?",
"success": "Procesado con éxito"
},
"label": {
"cancel": "(es)Cancel",
"headerTitle": "(es)ODMS Cloud",
"copyRight": "(es)OM Digital Solutions 2023",
"edit": "(es)Edit",
"save": "(es)Save",
"delete": "(es)Delete",
"return": "(es)Return",
"cancel": "Cancelar",
"copyRight": "© OM Digital Solutions Corporation",
"edit": "Editar",
"save": "Ahorrar",
"delete": "Delete",
"return": "Devolver",
"tier1": "(es)Admin",
"tier2": "(es)BC",
"tier3": "(es)Distributor",
"tier4": "(es)Dealer",
"tier5": "(es)Customer"
"tier5": "(es)Customer",
"notSelected": "(es)None"
}
},
"topPage": {
"message": {
"logout": "(es)ログイン有効期限が切れました。再度ログイン手続きを行ってください。"
"logout": "Su inicio de sesión ha caducado. Por favor inicie sesión nuevamente."
},
"label": {
"displayLanguage": "(es)Display language",
"languageEnglish": "(es)English",
"languageGerman": "(es)German",
"languageSpanish": "(es)Spanish",
"languageFrench": "(es)French",
"alreadyHaveAccount": "(es)Already have an account?",
"signInButton": "(es)Sign in",
"newUser": "(es)New user?",
"signUpButton": "(es)Create a new account",
"displayLanguage": "Idioma",
"languageEnglish": "English",
"languageGerman": "Deutsch",
"languageSpanish": "Español",
"languageFrench": "Français",
"alreadyHaveAccount": "Usuario existente",
"signInButton": "Iniciar sesión",
"newUser": "Nuevo usuario",
"signUpButton": "Crear una cuenta",
"logoAlt": "(es)OM Dictation Management System in the Cloud"
}
},
"signupPage": {
"message": {
"inputEmptyError": "(es)この項目の入力は必須です。入力してください。",
"passwordIncorrectError": "(es)入力されたパスワードがルールを満たしていません。下記のルールを満たすパスワードを入力してください。",
"emailIncorrectError": "(es)メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
"dealerNotFoundError": "(es)指定されたディーラーが見つかりませんでした。直接ディーラーを指定してください。"
"inputEmptyError": "Campo obligatorio",
"passwordIncorrectError": "La contraseña que ingresó no cumple con las reglas. Ingrese una contraseña que cumpla con las reglas a continuación.",
"emailIncorrectError": "El formato de la dirección de correo electrónico no es válido. Ingrese un formato de dirección de correo electrónico válido. (El sistema identifica correos electrónicos no válidos, como los que no tienen la marca o la parte con .com es algo que no parece correcto.)",
"dealerNotFoundError": "Hay un valor incorrecto en la selección del distribuidor. Por favor seleccione un distribuidor nuevamente."
},
"text": {
"title": "(es)Create your account",
"pageExplanation": "(es)Explanation...",
"accountInfoTitle": "(es)Register your account information",
"countryExplanation": "(es) Please select your country or the nearest country.",
"dealerExplanation": "(es)Please select the dealer to purchase a license.",
"adminInfoTitle": "(es)Register primary administrator's information",
"passwordTerms": "(es)Please set a password or issue an initial password.\nThe password must be more than 8 or less than 64 letters,numbers and symbols."
"title": "Crea tu cuenta",
"accountInfoTitle": "Información de Registro",
"countryExplanation": "Seleccione el país donde se encuentra. Si su país no aparece en la lista, seleccione el país más cercano.",
"dealerExplanation": "Seleccione el concesionario al que le gustaría comprar la licencia.",
"adminInfoTitle": "Registre la información del administrador principal",
"passwordTerms": "Establezca una contraseña. La contraseña debe tener entre 8 y 25 caracteres y debe contener letras, números y símbolos. (Debe enumerar el símbolo compatible e indicar si se necesita una letra mayúscula)."
},
"label": {
"company": "(es)Company Name",
"country": "(es)Country",
"dealer": "(es)Dealer",
"adminName": "(es)Admin Name",
"email": "(es)Email",
"password": "(es)Password",
"termsLink": "(es)Click here to read the terms of use",
"company": "Nombre de empresa",
"country": "País",
"dealer": "Concesionario (Opcional)",
"adminName": "Nombre del administrador",
"email": "Dirección de correo electrónico",
"password": "Contraseña",
"termsLink": "Haga clic aquí para leer el término de uso.",
"termsLinkFor": "(es)for ODDS.",
"termsCheckBox": "(es)Yes, I agree to the terms of use.",
"createAccountButton": "(es)Create account"
"termsCheckBox": "Sí, estoy de acuerdo con los términos de uso.",
"createAccountButton": "Entregar"
}
},
"signupConfirmPage": {
"message": {
"emailConflictError": "(es)このメールアドレスは既に登録されています。他のメールアドレスで登録してください。"
"emailConflictError": "Esta dirección de correo electrónico ya está registrada. Regístrese con otra dirección de correo electrónico."
},
"text": {
"title": "(es)Confirmation",
"pageExplanation": "(es)Explanation ......",
"accountInfoTitle": "(es)Your account information",
"adminInfoTitle": "(es)Primary administrator's information"
"title": "Verificación",
"pageExplanation": "Por favor verifique que la siguiente información sea correcta. Luego haga clic en Registro.",
"accountInfoTitle": "La información de tu cuenta",
"adminInfoTitle": "Información del administrador principal"
},
"label": {
"company": "(es)Company Name",
"country": "(es)Country",
"dealer": "(es)Dealer (Optional)",
"adminName": "(es)Admin Name",
"email": "(es)Email",
"password": "(es)Password",
"signupButton": "(es)Sign up"
"company": "Nombre de empresa",
"country": "País",
"dealer": "Concesionario (Opcional)",
"adminName": "Nombre del administrador",
"email": "Dirección de correo electrónico",
"password": "Contraseña",
"signupButton": "Registro"
}
},
"signupCompletePage": {
"text": {
"createdInfo": "(es)Your account has been created and a verification email has been set to your registered email address. Please click on the verification link included in the email to activate your account."
"createdInfo": "Se ha creado su cuenta y se ha enviado un correo electrónico de verificación a su dirección de correo electrónico registrada. Haga clic en el enlace de verificación en el correo electrónico para activar su cuenta."
},
"label": {
"title": "(es)Account created"
"title": "Cuenta creada"
}
},
"signupVerifyPage": {
"text": {
"verifySuccess": "(es)You have successfully verified the account.",
"faild": "(es)The verification url does not match. Please try again,\nor click on the link below to receive a new verification mail.",
"alreadySuccess": "(es)Your account has already been verified."
"verifySuccess": "Su cuenta ha sido verificada con éxito.",
"faild": "Fallo en la verificación. Haga clic en el siguiente enlace para volver a enviar el correo electrónico de confirmación.",
"alreadySuccess": "Tu cuenta ya ha sido verificada."
},
"label": {
"verifySuccess": "(es)Verified!",
"faild": "(es)Verification failed",
"alreadySuccess": "(es)Already Verified!",
"returnToSignIn": "(es)Return to Sign in"
"verifySuccess": "¡Verificada!",
"faild": "Fallo en la verificación",
"alreadySuccess": "¡Ya verificado!",
"returnToSignIn": "Haga clic para iniciar sesión"
}
},
"userListPage": {
"message": {
"addUserSuccess": "(es)メールアドレス宛に認証用メールを送信しました。",
"authorIdConflictError": "(es)このAuthor IDは既に登録されています。他のAuthor IDで登録してください。",
"authorIdIncorrectError": "(es)Author IDの形式が不正です。Author IDは半角英数字(大文字)と\"_\"のみ入力可能です。",
"roleChangeError": "(es)Roleの変更に失敗しました。画面を更新して再度ユーザー情報を取得してください。",
"encryptionPasswordCorrectError": "(es)EncryptionPasswordがルールを満たしていません。",
"alreadyLicenseDeallocatedError": "(es)すでにライセンス割り当てが解除されています。画面を更新して再度ご確認下さい。"
"addUserSuccess": "Se ha enviado un correo electrónico de verificación a su dirección de correo electrónico.",
"authorIdConflictError": "Este ID de autor ya ha sido registrado. Regístrese con otra identificación de autor.",
"authorIdIncorrectError": "El formato de ID del autor no es válido. Sólo se pueden ingresar caracteres alfanuméricos y \"_\" para la ID del autor.",
"roleChangeError": "No se puede cambiar la función de usuario. La información mostrada puede estar desactualizada, así que actualice la pantalla para ver el estado más reciente.",
"encryptionPasswordCorrectError": "La contraseña de cifrado no cumple con las reglas.",
"alreadyLicenseDeallocatedError": "La licencia asignada ya ha sido cancelada. La información mostrada puede estar desactualizada, así que actualice la pantalla para ver el estado más reciente."
},
"label": {
"title": "(es)User",
"addUser": "(es)Add User",
"licenseAllocation": "(es)License Allocation",
"name": "(es)Name",
"role": "(es)Role",
"authorID": "(es)Author ID",
"typistGroup": "(es)Typist Group",
"email": "(es)Email",
"status": "(es)Status",
"expiration": "(es)Expiration",
"remaining": "(es)Remaining",
"autoRenew": "(es)Auto renew",
"licenseAlert": "(es)License alert",
"notification": "(es)Notification",
"users": "(es)users",
"of": "(es)of",
"personal": "(es)Personal information",
"setting": "(es)Setting",
"selectGroup": "(es)Select group",
"addToGroup": "(es)Add to group (Optional)",
"author": "(es)Author",
"transcriptionist": "(es)Transcriptionist",
"encryption": "(es)Encryption",
"prompt": "(es)Prompt",
"emailVerified": "(es)Email Verified",
"editUser": "(es)Edit User",
"licenseDeallocation": "(es)License Deallocation",
"deleteUser": "(es)Delete User",
"none": "(es)None",
"encryptionPassword": "(es)Password:",
"encryptionPasswordTerm": "(es)Please set your password using 4 to 16 ASCII characters.\nYour password may include letters, numbers, and symbols."
"title": "Usuario",
"addUser": "Agregar usuario",
"licenseAllocation": "Asignar licencia",
"name": "Nombre",
"role": "Role",
"authorID": "ID de autor",
"typistGroup": "Grupo de transcriptor",
"email": "Email",
"status": "Estado",
"expiration": "Fecha de caducidad",
"remaining": "Período restante",
"autoRenew": "Renovación Automática",
"licenseAlert": "Alerta de licencia",
"notification": "Notificación",
"users": "Usuarios",
"of": "de",
"personal": "Información de usuario",
"setting": "Ajustes",
"selectGroup": "Selecciona grupo",
"addToGroup": "Agregar grupo (opcional)",
"author": "Autor",
"transcriptionist": "Transcriptor",
"encryption": "Codificación",
"prompt": "Solicitar",
"emailVerified": "Correo electrónico verificado",
"editUser": "Editar usuario",
"licenseDeallocation": "Desasignar licencia",
"deleteUser": "Borrar usuario",
"none": "Ninguno",
"encryptionPassword": "Contraseña",
"encryptionPasswordTerm": "Configure su contraseña utilizando de 4 a 16 símbolos alfanuméricos y."
}
},
"LicenseSummaryPage": {
"label": {
"title": "(es)License",
"subTitle": "(es)EFGI Legal",
"orderLicense": "(es)Order License",
"orderHistory": "(es)Order History",
"activateLicenseKey": "(es)Activate License Key",
"totalLicense": "(es)Total license",
"allocatedLicense": "(es)Allocated license",
"reusableLicense": "(es)Reusable license",
"freeLicense": "(es)Free license",
"expiringWithin14daysLicense": "(es)Expiring within 14days license",
"issueRequesting": "(es)Issue Requesting",
"numberOfRequesting": "(es)Number of Requesting",
"shortage": "(es)Shortage",
"storageSize": "(es)Storage Size",
"usedSize": "(es)Used Size",
"storageAvailable": "(es)Storage Available"
"title": "Licencia(s)",
"orderLicense": "Licencia de pedido",
"orderHistory": "Historial de pedidos",
"activateLicenseKey": "Activar clave de licencia",
"totalLicense": "Inventario de licencias",
"allocatedLicense": "Número de licencias asignadas",
"reusableLicense": "Número de licencias disponibles para reutilizar",
"freeLicense": "Número de licencias sin usar",
"expiringWithin14daysLicense": "Número de licencias que vencen en 14 días",
"issueRequesting": "Número total de licencias en pedido",
"numberOfRequesting": "Número total de pedidos",
"shortage": "Escasez",
"storageSize": "Almacenamiento disponible",
"usedSize": "Almacenamiento utilizado",
"storageAvailable": "Almacenamiento no disponible (cantidad excedida)"
}
},
"licenseOrderPage": {
"message": {
"inputEmptyError": "(es)この項目の入力は必須です。入力してください。",
"poNumberIncorrectError": "(es)PO Numberの形式が不正です。PO Numberは半角英数字(大文字)のみ入力可能です。",
"newOrderIncorrectError": "(es)New Orderには1以上の数字を入力してください。",
"confirmOrder": "(es)注文を行いますか?",
"poNumberConflictError": "(es)既に同じPO Numberで注文済みもしくは発行済みの注文が存在しています。他のPO Numberで注文してください。"
"inputEmptyError": "Campo obligatorio",
"poNumberIncorrectError": "El formato del número de orden de compra no es válido. Sólo se pueden ingresar caracteres alfanuméricos para el número de orden de compra.",
"newOrderIncorrectError": "Ingrese un número mayor o igual a 1 para el Nuevo Pedido.",
"confirmOrder": "¿Quieres hacer un pedido?",
"poNumberConflictError": "El número de orden de compra ingresado ya existe. Ingrese un número de orden de compra diferente."
},
"label": {
"title": "(es)Order License",
"licenses": "(es)Licenses",
"poNumber": "(es)PO Number",
"newOrder": "(es)New Order",
"orderButton": "(es)Order",
"licenseTypeText": "(es)One year"
"title": "Licencia de pedido",
"licenses": "Tipo de licencia",
"poNumber": "Número de orden de compra",
"newOrder": "Número de licencias",
"orderButton": "Orden",
"licenseTypeText": "Un año"
}
},
"dictationPage": {
"message": {
"noPlaybackAuthorization": "(es)本タスクをPlayBackできる権限がありません。",
"taskToPlaybackNoExists": "(es)タスクがすでに文字起こし完了済みまたは存在しないため、PlayBackできません。",
"taskNotEditable": "(es)すでに文字起こし作業着手中またはタスクが存在しないため、タイピストを変更できません。"
"noPlaybackAuthorization": "No tienes permiso para reproducir este archivo.",
"taskToPlaybackNoExists": "El archivo no se puede reproducir porque ya ha sido transcrito o no existe.",
"taskNotEditable": "No se puede cambiar el transcriptor porque la transcripción ya está en curso o el archivo no existe. Actualice la pantalla y verifique el estado más reciente."
},
"label": {
"title": "(es)Dictations",
"displayInfomation": "(es)Display Information",
"jobNumber": "(es)Job Number",
"status": "(es)Status",
"priority": "(es)Priority",
"encryption": "(es)Encryption",
"authorId": "(es)Author ID",
"workType": "(es)WorkType",
"fileName": "(es)File Name",
"fileLength": "(es)File Length",
"fileSize": "(es)File size",
"recordingStartedDate": "(es)Recording started date",
"recordingFinishedDate": "(es)Recording finished date",
"uploadDate": "(es)Upload date",
"transcriptionStartedDate": "(es)Transcription start date",
"transcriptionFinishedDate": "(es)Transcription finished date",
"transcriptionist": "(es)Transcriptionist",
"comment": "(es)Comment",
"optionItem1": "(es)OptionItem1",
"optionItem2": "(es)OptionItem2",
"optionItem3": "(es)OptionItem3",
"optionItem4": "(es)OptionItem4",
"optionItem5": "(es)OptionItem5",
"optionItem6": "(es)OptionItem6",
"optionItem7": "(es)OptionItem7",
"optionItem8": "(es)OptionItem8",
"optionItem9": "(es)OptionItem9",
"optionItem10": "(es)OptionItem10",
"filter": "(es)Filter",
"uploaded": "(es)Uploaded",
"inProgress": "(es)InProgress",
"finished": "(es)Finished",
"pending": "(es)Pending",
"backup": "(es)Backup",
"playback": "(es)Playback",
"fileProperty": "(es)File Property",
"changeTranscriptionist": "(es)Change Transcriptionist",
"deleteDictation": "(es)Delete Dictation",
"selectedTranscriptionist": "(es)Selected",
"poolTranscriptionist": "(es)Pool"
"title": "Dictado",
"displayInfomation": "Informationen anzeigen",
"jobNumber": "Número de trabajo",
"status": "Estado",
"priority": "Prioridad",
"encryption": "Codificación",
"authorId": "ID de autor",
"workType": "ID de tipo de trabajo",
"fileName": "Nombre de archivo",
"fileLength": "Longitud del archivo",
"fileSize": "Tamaño de archivo",
"recordingStartedDate": "Fecha de inicio del dictado",
"recordingFinishedDate": "Fecha completa del dictado",
"uploadDate": "Fecha de carga del dictado",
"transcriptionStartedDate": "Fecha de inicio de la transcripción",
"transcriptionFinishedDate": "Fecha de finalización de la transcripción",
"transcriptionist": "Transcriptor",
"comment": "Comentario",
"optionItem1": "Elemento opcional 1",
"optionItem2": "Elemento opcional 2",
"optionItem3": "Elemento opcional 3",
"optionItem4": "Elemento opcional 4",
"optionItem5": "Elemento opcional 5",
"optionItem6": "Elemento opcional 6",
"optionItem7": "Elemento opcional 7",
"optionItem8": "Elemento opcional 8",
"optionItem9": "Elemento opcional 9",
"optionItem10": "Elemento opcional 10",
"filter": "Filtro",
"uploaded": "subido",
"inProgress": "En curso",
"finished": "Finalizado",
"pending": "Pendiente",
"backup": "Copia de seguridad",
"playback": "Reproducir",
"fileProperty": "Propiedad de archivo",
"changeTranscriptionist": "Cambiar transcriptor",
"deleteDictation": "Borrar dictado",
"selectedTranscriptionist": "Transcriptor seleccionado",
"poolTranscriptionist": "Lista de transcriptor"
}
},
"cardLicenseIssuePopupPage": {
"label": {
"title": "(es)License Card",
"subTitle": "(es)Card License Creation",
"number": "(es)Number of create licenses",
"createButton": "(es)Create"
"title": "Tarjeta de licencia",
"subTitle": "Creación de licencia de tarjeta",
"number": "Número de licencias",
"createButton": "Crear"
},
"message": {
"createSuccess": "(es)処理に成功しました。ファイルのダウンロードが完了するまでお待ちください。",
"createNumberIncorrectError": "(es)Number of create licensesには1以上の数字を入力してください。"
"createSuccess": "El procesamiento fue exitoso.",
"createNumberIncorrectError": "Introduzca un número de 1 o más en Número de licencias."
}
},
"partnerLicensePage": {
"label": {
"cardLicenseButton": "(es)License Card"
"cardLicenseButton": "Tarjeta de licencia"
}
},
"cardLicenseActivatePopupPage": {
"label": {
"title": "(es)Activate License Key",
"keyNumber": "(es)Key number",
"activateButton": "(es)activate",
"keyNumberIncorrectError": "(es)Key Numberには20桁の半角大文字英数字を入力してください。"
"title": "Activar clave de licencia",
"keyNumber": "Clave",
"activateButton": "Activar",
"keyNumberIncorrectError": "Por favor ingrese la clave alfanumérica de 20 caracteres."
},
"message": {
"LicenseKeyNotExistError": "(es)入力されたライセンスキーは存在しません。ライセンスキーを再度お確かめください。",
"LicenseKeyAlreadyActivatedError": "(es)入力されたライセンスキーは、既に有効化されています。ライセンスキーを再度お確かめください。"
"LicenseKeyNotExistError": "La clave de licencia que ingresó no existe. Por favor verifique su clave de licencia nuevamente.",
"LicenseKeyAlreadyActivatedError": "La clave de licencia que ingresó ya está activada. Intente utilizar una clave de licencia diferente."
}
},
"addPartnerAccountPopupPage": {
"message": {
"addAccountSuccess": "(es)メールアドレス宛に認証用メールを送信しました。",
"inputEmptyError": "(es)この項目の入力は必須です。入力してください。",
"emailIncorrectError": "(es)メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
"emailConflictError": "(es)このメールアドレスは既に登録されています。他のメールアドレスで登録してください。"
"addAccountSuccess": "Se ha enviado un correo electrónico de verificación a su dirección de correo electrónico.",
"inputEmptyError": "Campo obligatorio",
"emailIncorrectError": "El formato de la dirección de correo electrónico no es válido. Ingrese un formato de dirección de correo electrónico válido. (El sistema identifica correos electrónicos no válidos, como los que no tienen la marca o la parte con .com es algo que no parece correcto.)",
"emailConflictError": "Esta dirección de correo electrónico ya está registrada. Regístrese con otra dirección de correo electrónico."
},
"text": {
"title": "(es)Add account",
"accountInfoTitle": "(es)Register account information",
"countryExplanation": "(es)Please select country or the nearest country.",
"adminInfoTitle": "(es)Register primary administrator's information"
"title": "Añadir cuenta",
"accountInfoTitle": "Registrar información de cuenta",
"countryExplanation": "Seleccione el país donde se encuentra. Si su país no aparece en la lista, seleccione el país más cercano.",
"adminInfoTitle": "Registre la información del administrador principal"
},
"label": {
"company": "(es)Company Name",
"country": "(es)Country",
"adminName": "(es)Admin Name",
"email": "(es)Email",
"createAccountButton": "(es)Add account"
"company": "Nombre de empresa",
"country": "País",
"adminName": "Nombre del administrador",
"email": "Email",
"createAccountButton": "Añadir cuenta"
}
},
"partnerLicense": {
"label": {
"title": "(es)License",
"subTitle": "(es)License for partners",
"orderLicenseButton": "(es)Order License",
"orderHistoryButton": "(es)Order History",
"IssueLicenseCardButton": "(es)License Card",
"displayCardHistoryButton": "(es)Card History",
"name": "(es)Name",
"category": "(es)Category",
"accountId": "(es)Account ID",
"stockLicense": "(es)Stock license",
"issueRequested": "(es)Issue Requested",
"shortage": "(es)Shortage",
"issueRequesting": "(es)Issue Requesting",
"viewDetails": "(es)View details",
"accounts": "(es)accounts"
"title": "Licencia",
"subTitle": "Licencias de socios",
"orderLicenseButton": "Licencia de pedido",
"orderHistoryButton": "Historial de pedidos",
"IssueLicenseCardButton": "Tarjeta de licencia",
"displayCardHistoryButton": "Historial de la tarjeta",
"name": "Nombre de empresa",
"category": "Nivel de cuenta",
"accountId": "ID de autor",
"stockLicense": "Inventario de licencias",
"issueRequested": "Licencias solicitadas",
"shortage": "Escasez de licencias",
"issueRequesting": "Licencias en Pedido",
"viewDetails": "Ver detalles",
"accounts": "cuentas"
}
},
"orderHistoriesPage": {
"label": {
"title": "(es)License",
"orderHistory": "(es)Order History",
"orderDate": "(es)Order date",
"issueDate": "(es)Issue date",
"numberOfOrder": "(es)Number of Order",
"poNumber": "(es)PO Number",
"status": "(es)Status",
"issueRequesting": "(es)Issue Requesting",
"issued": "(es)Issued",
"orderCanceled": "(es)Order Canceled",
"issue": "(es)Issue",
"issueCancel": "(es)Issue Cancel",
"orderCancel": "(es)Order Cancel",
"histories": "(es)histories"
"title": "Licencia",
"orderHistory": "Historial de pedidos",
"orderDate": "Fecha de pedido",
"issueDate": "Fecha de asunto",
"numberOfOrder": "Número de licencias en pedido",
"poNumber": "Número de orden de compra",
"status": "Estado",
"issueRequesting": "Licencias en Pedido",
"issued": "Licencia emitida",
"orderCanceled": "Orden cancelada",
"issue": "Emitida",
"issueCancel": "Cancelar licencias",
"orderCancel": "Cancelar pedido",
"histories": "historias"
},
"message": {
"notEnoughOfNumberOfLicense": "(es)ライセンスが不足しているため、発行することができませんでした。ライセンスの注文を行ってください。",
"alreadyIssueLicense": "(es)すでに発行済みの注文です。画面を更新してください。",
"alreadyLicenseIssueOrCancel": "(es)ライセンス注文のキャンセルに失敗しました。選択された注文はすでに発行またはキャンセルされています。画面を更新して再度ご確認ください。",
"alreadyLicenseStatusChanged": "(es)ライセンス発行のキャンセルに失敗しました。選択された注文の状態が変更されています。画面を更新して再度ご確認ください。",
"expiredSinceIssued": "(es)発行日から一定期間経過しているため、発行キャンセルできません。",
"alreadyLicenseAllocated": "(es)発行したライセンスがすでに割り当てられたため、発行キャンセルできません。"
"notEnoughOfNumberOfLicense": "No se pudieron emitir licencias debido a una cantidad insuficiente de licencias. Solicite licencias adicionales.",
"alreadyIssueLicense": "El pedido ya ha sido realizado. La información mostrada puede estar desactualizada, así que actualice la pantalla para ver el estado más reciente.",
"alreadyLicenseIssueOrCancel": "No se pudo cancelar el pedido de licencia. La licencia para el pedido seleccionado ya ha sido emitida o el pedido ya ha sido cancelado. Actualice la pantalla y verifique nuevamente.",
"alreadyLicenseStatusChanged": "No se pudo cancelar la emisión de la licencia. El estado del pedido seleccionado ha cambiado. Actualice la pantalla y verifique nuevamente.",
"expiredSinceIssued": "Las licencias que hayan sido emitidas no podrán cancelarse por haber transcurrido más de 14 días desde la fecha de emisión.",
"alreadyLicenseAllocated": "La licencia emitida no se puede cancelar porque ya ha sido asignada."
}
},
"allocateLicensePopupPage": {
"label": {
"title": "(es)License Allocation",
"personalInformation": "(es)Personal Information",
"email": "(es)Email",
"name": "(es)Name",
"authorID": "(es)Author ID",
"status": "(es)Status",
"allocated": "(es)Allocated",
"notAllocated": "(es)Not Allocated",
"expirationDate": "(es)Expiration date",
"licenseInformation": "(es)License Information",
"licenseAvailable": "(es)License available",
"dropDownHeading": "(es)Select a license.",
"oneYear": "(es)One Year",
"allocateLicense": "(es)OK"
"title": "Asignar licencia",
"email": "Email",
"name": "Nombre",
"authorID": "ID de autor",
"status": "Estado",
"allocated": "Licencia asignada",
"notAllocated": "Licencia no asignada",
"expirationDate": "Fecha de caducidad",
"licenseInformation": "Información de licencia",
"licenseAvailable": "Licencia disponible",
"dropDownHeading": "Seleccionar licencia",
"oneYear": "Un año",
"allocateLicense": "Aceptar"
},
"message": {
"inputEmptyError": "(es)この項目の入力は必須です。入力してください。",
"licenseAllocationFailure": "(es)ライセンスの割り当てに失敗しました。他のライセンスを選択して再度割り当てをしてください。"
"inputEmptyError": "Campo obligatorio",
"licenseAllocationFailure": "No se pudo asignar la licencia. La información mostrada puede estar desactualizada, así que actualice la pantalla para ver el estado más reciente."
}
},
"workflowPage": {
"label": {
"title": "(es)Workflow"
"title": "flujo de trabajo"
}
},
"typistGroupSetting": {
"label": {
"title": "(es)Transctiprionist Group",
"addGroup": "(es)Add Group",
"groupName": "(es)Group Name",
"addTypistGroup": "(es)Add Transcriptionist Group",
"transcriptionist": "(es)Transcriptionist",
"selected": "(es)Selected",
"pool": "(es)Pool",
"add": "(es)Add",
"remove": "(es)Remove",
"editTypistGroup": "(es)Edit Transcriptionist Group"
"title": "Grupo de transcriptor",
"addGroup": "Agregar grupo",
"groupName": "Nombre del grupo",
"addTypistGroup": "Agregar grupo transcriptor",
"transcriptionist": "Transcriptor",
"selected": "Transcriptor seleccionado",
"pool": "Lista de transcriptor",
"add": "Añadir",
"remove": "Eliminar",
"editTypistGroup": "Editar grupo transcriptor"
},
"message": {
"selectedTypistEmptyError": "(es)TranscriptionistがいないTranscriptionistGroupは保存できません。1名以上をTranscriptionistとして選択してください。",
"groupSaveFailedError": "(es)TypistGroupの保存に失敗しました。画面を更新し、再度実行してください"
"selectedTypistEmptyError": "Se deben seleccionar uno o más transcriptores para guardar un grupo de transcripción.",
"groupSaveFailedError": "No se pudo guardar el grupo mecanógrafo. La información mostrada puede estar desactualizada, así que actualice la pantalla para ver el estado más reciente."
}
},
"worktypeIdSetting": {
"label": {
"title": "(es)Worktype ID Setting",
"activeWorktypeId": "(es)Active Worktype ID",
"addWorktypeId": "(es)Add Worktype ID",
"worktypeId": "(es)Worktype ID",
"description": "(es)Description",
"descriptionOptional": "(es)Description (Optional)",
"optionItem": "(es)Option Item",
"worktypeIdTerms": "(es)WorktypeID should be alphanumeric and symbols,\nbut not include: \\ / : * ? “ < > | .",
"addWorktype": "(es)Add Worktype",
"editWorktypeId": "(es)Edit Worktype ID",
"saveChange": "(es)Save Changes",
"title": "Configuración de ID de tipo de trabajo",
"activeWorktypeId": "ID de tipo de trabajo activo",
"addWorktypeId": "Agregar ID de tipo de trabajo",
"worktypeId": "ID de tipo de trabajo",
"description": "Descripción",
"descriptionOptional": "Descripción (Opcional)",
"optionItem": "Elemento opcional",
"worktypeIdTerms": "El ID del tipo de trabajo puede contener símbolos alfanuméricos y. No se pueden utilizar los siguientes símbolos:\\/:*?\"<>|.",
"addWorktype": "Agregar tipo de trabajo",
"editWorktypeId": "Editar ID de tipo de trabajo",
"saveChange": "Guardar cambios",
"editOptionItems": "(es)Option Item",
"itemLabel": "(es)Item label",
"defaultValue": "(es)Default value",
@ -404,28 +400,40 @@
"optionItemTerms": "(es)The Item label and Initial value should be alphanumeric and symbols, but not include: \\ / : * ? “ < > | ."
},
"message": {
"worktypeIdIncorrectError": "(es)入力されたWorktypeIDがルールを満たしていません。下記のルールを満たすWorktypeIDを入力してください",
"alreadyWorktypeIdExistError": "(es)このWorktype IDは既に登録されています。他のWorktype IDで登録してください。",
"worktypeIDLimitError": "(es)Worktype IDが登録件数の上限に達しているため追加できません。",
"worktypeIdIncorrectError": "El ID de tipo de trabajo que ingresó no cumple con las especificaciones. Ingrese el tipo de trabajo correcto como se describe en las especificaciones a continuación.",
"alreadyWorktypeIdExistError": "Este ID de tipo de trabajo está registrado actualmente. Regístrese con una ID de tipo de trabajo diferente.",
"worktypeIDLimitError": "No se puede agregar el ID de tipo de trabajo porque ha alcanzado el número máximo de registros.",
"optionItemInvalidError": "(es)Default valueがDefaultに設定されている場合、Initial valueは入力が必須です。",
"optionItemSaveFailedError": "(es)オプションアイテムの保存に失敗しました。画面を更新し、再度実行してください",
"optionItemIncorrectError": "(es)入力されたItem labelまたはInitial valueがルールを満たしていません。下記のルールを満たす値を入力してください",
"updateActiveWorktypeFailedError": "(es)Active WorktypeIDの保存に失敗しました。画面を更新し、再度実行してください"
}
},
"templateFilePage": {
"label": {
"title": "(es)Template List",
"addTemplate": "(es)Add Template",
"fileName": "(es)Flie Name",
"chooseFile": "(es)Choose File",
"notFileChosen": "(es)- Not file chosen -",
"fileSizeTerms": "(es)ファイルサイズは5MBまでです。",
"fileSizeError": "(es)選択されたファイルのサイズが大きすぎます。サイズが5MB以下のファイルを選択してください。",
"fileEmptyError": "(es)ファイル選択は必須です。ファイルを選択してください。"
}
},
"partnerPage": {
"label": {
"title": "(es)Partners",
"addAccount": "(es)Add Account",
"name": "(es)Name",
"category": "(es)Category",
"accountId": "(es)Account ID",
"country": "(es)Country",
"primaryAdmin": "(es)Primary Admin",
"email": "(es)E-mail",
"dealerManagement": "(es)Dealer Management",
"partners": "(es)partners",
"deleteAccount": "(es)Delete Account"
"title": "Socios",
"addAccount": "Añadir cuenta",
"name": "Nombre de empresa",
"category": "Nivel de cuenta",
"accountId": "ID de autor",
"country": "País",
"primaryAdmin": "Administrador primario",
"email": "Email",
"dealerManagement": "Permitir que el distribuidor realice los cambios",
"partners": "Socios",
"deleteAccount": "Borrar cuenta"
}
},
"accountPage": {
@ -451,5 +459,14 @@
"message": {
"updateAccountFailedError": "(es)アカウント情報の保存に失敗しました。画面を更新し、再度実行してください"
}
},
"deleteAccountPopup": {
"label": {
"title": "(es)Delete Account",
"subTitle": "(es)Delete your account",
"cautionOfDeleteingAccountData": "(es)Deleting your account will remove all of your audio files and\nlicenses from system. and you'll cannot use ODMS Cloud.\nThis cannot be undone.",
"deleteButton": "(es)Delete account",
"cancelButton": "(es)Cancel"
}
}
}

View File

@ -1,399 +1,395 @@
{
"common": {
"message": {
"inputEmptyError": "(fr)この項目の入力は必須です。入力してください。",
"passwordIncorrectError": "(fr)Error Message",
"emailIncorrectError": "(fr)Error Message",
"internalServerError": "(fr)処理に失敗しました。時間をおいて再実行しても解決しない場合はシステム管理者にお問い合わせください。",
"permissionDeniedError": "(fr)操作を実行する権限がありません。",
"listEmpty": "(fr)検索結果が0件です。",
"dialogConfirm": "(fr)操作を実行しますか?",
"success": "(fr)処理に成功しました。"
"inputEmptyError": "Champ obligatoire",
"passwordIncorrectError": "(fr)入力されたパスワードがルールを満たしていません。下記のルールを満たすパスワードを入力してください。",
"emailIncorrectError": "(fr)メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
"internalServerError": "Le traitement a échoué. Veuillez réessayer plus tard.",
"listEmpty": "Il y a 0 résultats de recherche.",
"dialogConfirm": "Voulez-vous effectuer l'opération?",
"success": "Traité avec succès"
},
"label": {
"cancel": "(fr)Cancel",
"headerTitle": "(fr)ODMS Cloud",
"copyRight": "(fr)OM Digital Solutions 2023",
"edit": "(fr)Edit",
"save": "(fr)Save",
"delete": "(fr)Delete",
"return": "(fr)Return",
"cancel": "Annuler",
"copyRight": "© OM Digital Solutions Corporation",
"edit": "Éditer",
"save": "Sauvegarder",
"delete": "Delete",
"return": "Retour",
"tier1": "(fr)Admin",
"tier2": "(fr)BC",
"tier3": "(fr)Distributor",
"tier4": "(fr)Dealer",
"tier5": "(fr)Customer"
"tier5": "(fr)Customer",
"notSelected": "(fr)None"
}
},
"topPage": {
"message": {
"logout": "(fr)ログイン有効期限が切れました。再度ログイン手続きを行ってください。"
"logout": "Votre connexion a expiré. Veuillez vous reconnecter."
},
"label": {
"displayLanguage": "(fr)Display language",
"languageEnglish": "(fr)English",
"languageGerman": "(fr)German",
"languageSpanish": "(fr)Spanish",
"languageFrench": "(fr)French",
"alreadyHaveAccount": "(fr)Already have an account?",
"signInButton": "(fr)Sign in",
"newUser": "(fr)New user?",
"signUpButton": "(fr)Create a new account",
"displayLanguage": "Langue",
"languageEnglish": "English",
"languageGerman": "Deutsch",
"languageSpanish": "Español",
"languageFrench": "Français",
"alreadyHaveAccount": "Utilisateur existant",
"signInButton": "S'identifier",
"newUser": "Nouvel utilisateur",
"signUpButton": "Créer un compte",
"logoAlt": "(fr)OM Dictation Management System in the Cloud"
}
},
"signupPage": {
"message": {
"inputEmptyError": "(fr)この項目の入力は必須です。入力してください。",
"passwordIncorrectError": "(fr)入力されたパスワードがルールを満たしていません。下記のルールを満たすパスワードを入力してください。",
"emailIncorrectError": "(fr)メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
"dealerNotFoundError": "(fr)指定されたディーラーが見つかりませんでした。直接ディーラーを指定してください。"
"inputEmptyError": "Champ obligatoire",
"passwordIncorrectError": "Le mot de passe que vous avez saisi ne respecte pas les règles. Veuillez saisir un mot de passe qui répond aux règles ci-dessous.",
"emailIncorrectError": "Le format de l'adresse e-mail n'est pas valide. Veuillez saisir un format d'adresse e-mail valide. (Le système identifie les e-mails non valides, par exemple sans la marque ou la partie avec .com, cela ne semble pas correct.)",
"dealerNotFoundError": "Il y a une valeur incorrecte dans la sélection du revendeur. Veuillez sélectionner à nouveau un revendeur."
},
"text": {
"title": "(fr)Create your account",
"pageExplanation": "(fr)Explanation...",
"accountInfoTitle": "(fr)Register your account information",
"countryExplanation": "(fr) Please select your country or the nearest country.",
"dealerExplanation": "(fr)Please select the dealer to purchase a license.",
"adminInfoTitle": "(fr)Register primary administrator's information",
"passwordTerms": "(fr)Please set a password or issue an initial password.\nThe password must be more than 8 or less than 64 letters,numbers and symbols."
"title": "Créez votre compte",
"accountInfoTitle": "Information d'inscription",
"countryExplanation": "Sélectionnez le pays où vous vous trouvez. Si votre pays ne figure pas dans la liste, veuillez sélectionner le pays le plus proche.",
"dealerExplanation": "Veuillez sélectionner le concessionnaire auprès duquel vous souhaitez acheter la licence.",
"adminInfoTitle": "Enregistrer les informations de l'administrateur principal",
"passwordTerms": "Veuillez définir un mot de passe. Le mot de passe doit être composé de 8 à 25 caractères et doit contenir des lettres, des chiffres et des symboles. (Devrait lister les symboles compatibles et indiquer si une majuscule est nécessaire)."
},
"label": {
"company": "(fr)Company Name",
"country": "(fr)Country",
"dealer": "(fr)Dealer",
"adminName": "(fr)Admin Name",
"email": "(fr)Email",
"password": "(fr)Password",
"termsLink": "(fr)Click here to read the terms of use",
"company": "Nom de l'entreprise",
"country": "Pays",
"dealer": "Concessionnaire (Facultatif)",
"adminName": "Nom de l'administrateur",
"email": "Adresse e-mail",
"password": "Mot de passe",
"termsLink": "Cliquez ici pour lire les conditions d'utilisation.",
"termsLinkFor": "(fr)for ODDS.",
"termsCheckBox": "(fr)Yes, I agree to the terms of use.",
"createAccountButton": "(fr)Create account"
"termsCheckBox": "Oui, j'accepte les conditions d'utilisation.",
"createAccountButton": "Soumettre"
}
},
"signupConfirmPage": {
"message": {
"emailConflictError": "(fr)このメールアドレスは既に登録されています。他のメールアドレスで登録してください。"
"emailConflictError": "Cette adresse email est déjà enregistrée. Veuillez vous inscrire avec une autre adresse e-mail."
},
"text": {
"title": "(fr)Confirmation",
"pageExplanation": "(fr)Explanation ......",
"accountInfoTitle": "(fr)Your account information",
"adminInfoTitle": "(fr)Primary administrator's information"
"title": "Vérification",
"pageExplanation": "Veuillez vérifier que les informations ci-dessous sont correctes. Cliquez ensuite sur S'inscrire.",
"accountInfoTitle": "Informations sur votre compte",
"adminInfoTitle": "Informations sur l'administrateur principal"
},
"label": {
"company": "(fr)Company Name",
"country": "(fr)Country",
"dealer": "(fr)Dealer (Optional)",
"adminName": "(fr)Admin Name",
"email": "(fr)Email",
"password": "(fr)Password",
"signupButton": "(fr)Sign up"
"company": "Nom de l'entreprise",
"country": "Pays",
"dealer": "Concessionnaire (Facultatif)",
"adminName": "Nom de l'administrateur",
"email": "Adresse e-mail",
"password": "Mot de passe",
"signupButton": "S'inscrire."
}
},
"signupCompletePage": {
"text": {
"createdInfo": "(fr)Your account has been created and a verification email has been set to your registered email address. Please click on the verification link included in the email to activate your account."
"createdInfo": "Votre compte a été créé et un e-mail de vérification a été envoyé à votre adresse e-mail enregistrée. Veuillez cliquer sur le lien de vérification dans l'e-mail pour activer votre compte."
},
"label": {
"title": "(fr)Account created"
"title": "Compte créé"
}
},
"signupVerifyPage": {
"text": {
"verifySuccess": "(fr)You have successfully verified the account.",
"faild": "(fr)The verification url does not match. Please try again,\nor click on the link below to receive a new verification mail.",
"alreadySuccess": "(fr)Your account has already been verified."
"verifySuccess": "Votre compte a été vérifié avec succès.",
"faild": "Échec de la vérification. Veuillez cliquer sur le lien ci-dessous pour renvoyer l'e-mail de confirmation.",
"alreadySuccess": "Votre compte a déjà été vérifié."
},
"label": {
"verifySuccess": "(fr)Verified!",
"faild": "(fr)Verification failed",
"alreadySuccess": "(fr)Already Verified!",
"returnToSignIn": "(fr)Return to Sign in"
"verifySuccess": "Vérifié !",
"faild": "Échec de la vérification",
"alreadySuccess": "Déjà vérifié !",
"returnToSignIn": "Cliquez pour vous š'identifier"
}
},
"userListPage": {
"message": {
"addUserSuccess": "(fr)メールアドレス宛に認証用メールを送信しました。",
"authorIdConflictError": "(fr)このAuthor IDは既に登録されています。他のAuthor IDで登録してください。",
"authorIdIncorrectError": "(fr)Author IDの形式が不正です。Author IDは半角英数字(大文字)と\"_\"のみ入力可能です。",
"roleChangeError": "(fr)Roleの変更に失敗しました。画面を更新して再度ユーザー情報を取得してください。",
"encryptionPasswordCorrectError": "(fr)EncryptionPasswordがルールを満たしていません。",
"alreadyLicenseDeallocatedError": "(fr)すでにライセンス割り当てが解除されています。画面を更新して再度ご確認下さい。"
"addUserSuccess": "Un e-mail de vérification a été envoyé à votre adresse e-mail.",
"authorIdConflictError": "Cet identifiant d'auteur a déjà été enregistré. Veuillez vous inscrire avec un autre identifiant d'auteur.",
"authorIdIncorrectError": "Le format de l'identifiant de l'auteur n'est pas valide. Seuls les caractères alphanumériques et \"_\" peuvent être saisis pour l'ID d'auteur.",
"roleChangeError": "Impossible de modifier le rôle de l'utilisateur. Les informations affichées peuvent être obsolètes, veuillez donc actualiser l'écran pour voir le dernier statut.",
"encryptionPasswordCorrectError": "Le mot de passe de cryptage n'est pas conforme aux règles.",
"alreadyLicenseDeallocatedError": "La licence attribuée a déjà été annulée. Les informations affichées peuvent être obsolètes, veuillez donc actualiser l'écran pour voir le dernier statut."
},
"label": {
"title": "(fr)User",
"addUser": "(fr)Add User",
"licenseAllocation": "(fr)License Allocation",
"name": "(fr)Name",
"role": "(fr)Role",
"authorID": "(fr)Author ID",
"typistGroup": "(fr)Typist Group",
"email": "(fr)Email",
"status": "(fr)Status",
"expiration": "(fr)Expiration",
"remaining": "(fr)Remaining",
"autoRenew": "(fr)Auto renew",
"licenseAlert": "(fr)License alert",
"notification": "(fr)Notification",
"users": "(fr)users",
"of": "(fr)of",
"personal": "(fr)Personal information",
"setting": "(fr)Setting",
"selectGroup": "(fr)Select group",
"addToGroup": "(fr)Add to group (Optional)",
"author": "(fr)Author",
"transcriptionist": "(fr)Transcriptionist",
"encryption": "(fr)Encryption",
"prompt": "(fr)Prompt",
"emailVerified": "(fr)Email Verified",
"editUser": "(fr)Edit User",
"licenseDeallocation": "(fr)License Deallocation",
"deleteUser": "(fr)Delete User",
"none": "(fr)None",
"encryptionPassword": "(fr)Password:",
"encryptionPasswordTerm": "(fr)Please set your password using 4 to 16 ASCII characters.\nYour password may include letters, numbers, and symbols."
"title": "Utilisateur",
"addUser": "Ajouter un utilisateur",
"licenseAllocation": "Attribuer une licence",
"name": "Nom",
"role": "le",
"authorID": "Identifiant Auteur",
"typistGroup": "Groupe de transcriptionniste",
"email": "Email",
"status": "État",
"expiration": "Date d'expiration",
"remaining": "Période restante",
"autoRenew": "Renouvellement automatique",
"licenseAlert": "Alerte de licence",
"notification": "Notification",
"users": "Utilisateurs",
"of": "de",
"personal": "Informations sur lutilisateur",
"setting": "Paramètres",
"selectGroup": "Sélectionnez le groupe",
"addToGroup": "Ajouter groupe (facultatif)",
"author": "Auteur",
"transcriptionist": "Transcriptionniste",
"encryption": "Chiffrement",
"prompt": "Invite",
"emailVerified": "Email verifié",
"editUser": "Modifier l'utilisateur",
"licenseDeallocation": "Annuler l'attribution de la licence",
"deleteUser": "Supprimer l'utilisateur",
"none": "Aucun",
"encryptionPassword": "Mot de passe",
"encryptionPasswordTerm": "Veuillez définir votre mot de passe en utilisant 4 à 16 caractères alphanumériques et symboles."
}
},
"LicenseSummaryPage": {
"label": {
"title": "(fr)License",
"subTitle": "(fr)EFGI Legal",
"orderLicense": "(fr)Order License",
"orderHistory": "(fr)Order History",
"activateLicenseKey": "(fr)Activate License Key",
"totalLicense": "(fr)Total license",
"allocatedLicense": "(fr)Allocated license",
"reusableLicense": "(fr)Reusable license",
"freeLicense": "(fr)Free license",
"expiringWithin14daysLicense": "(fr)Expiring within 14days license",
"issueRequesting": "(fr)Issue Requesting",
"numberOfRequesting": "(fr)Number of Requesting",
"shortage": "(fr)Shortage",
"storageSize": "(fr)Storage Size",
"usedSize": "(fr)Used Size",
"storageAvailable": "(fr)Storage Available"
"title": "Licence(s)",
"orderLicense": "Commander licence",
"orderHistory": "Historique des commandes",
"activateLicenseKey": "Activer la clé de licence",
"totalLicense": "Inventaire des licences",
"allocatedLicense": "Nombre de licences attribuées",
"reusableLicense": "Nombre de licences disponibles pour réutilisation",
"freeLicense": "Nombre de licences inutilisées",
"expiringWithin14daysLicense": "Nombre de licences expirant dans les 14 jours",
"issueRequesting": "Nombre total de licences commandées",
"numberOfRequesting": "Nombre total de commandes",
"shortage": "Pénurie",
"storageSize": "Stockage disponible",
"usedSize": "Stockage utilisé",
"storageAvailable": "Stockage indisponible (montant dépassée)"
}
},
"licenseOrderPage": {
"message": {
"inputEmptyError": "(fr)この項目の入力は必須です。入力してください。",
"poNumberIncorrectError": "(fr)PO Numberの形式が不正です。PO Numberは半角英数字(大文字)のみ入力可能です。",
"newOrderIncorrectError": "(fr)New Orderには1以上の数字を入力してください。",
"confirmOrder": "(fr)注文を行いますか?",
"poNumberConflictError": "(fr)既に同じPO Numberで注文済みもしくは発行済みの注文が存在しています。他のPO Numberで注文してください。"
"inputEmptyError": "Champ obligatoire",
"poNumberIncorrectError": "Le format du numéro de bon de commande n'est pas valide. Seuls des caractères alphanumériques peuvent être saisis pour le numéro de bon de commande.",
"newOrderIncorrectError": "Veuillez saisir un nombre supérieur ou égal à 1 pour la nouvelle commande.",
"confirmOrder": "Voulez-vous passer commande?",
"poNumberConflictError": "Le numéro de bon de commande saisi existe déjà. Veuillez saisir un autre numéro de bon de commande."
},
"label": {
"title": "(fr)Order License",
"licenses": "(fr)Licenses",
"poNumber": "(fr)PO Number",
"newOrder": "(fr)New Order",
"orderButton": "(fr)Order",
"licenseTypeText": "(fr)One year"
"title": "Commander licence",
"licenses": "Type de licence",
"poNumber": "Numéro de bon de commande",
"newOrder": "Nombre de licences",
"orderButton": "Commande",
"licenseTypeText": "Un ans"
}
},
"dictationPage": {
"message": {
"noPlaybackAuthorization": "(fr)本タスクをPlayBackできる権限がありません。",
"taskToPlaybackNoExists": "(fr)タスクがすでに文字起こし完了済みまたは存在しないため、PlayBackできません。",
"taskNotEditable": "(fr)すでに文字起こし作業着手中またはタスクが存在しないため、タイピストを変更できません。"
"noPlaybackAuthorization": "Vous n'êtes pas autorisé à lire ce fichier.",
"taskToPlaybackNoExists": "Le fichier ne peut pas être lu car il a déjà été transcrit ou n'existe pas.",
"taskNotEditable": "Le transcripteur ne peut pas être changé car la transcription est déjà en cours ou le fichier n'existe pas. Veuillez actualiser l'écran et vérifier le dernier statut."
},
"label": {
"title": "(fr)Dictations",
"displayInfomation": "(fr)Display Information",
"jobNumber": "(fr)Job Number",
"status": "(fr)Status",
"priority": "(fr)Priority",
"encryption": "(fr)Encryption",
"authorId": "(fr)Author ID",
"workType": "(fr)WorkType",
"fileName": "(fr)File Name",
"fileLength": "(fr)File Length",
"fileSize": "(fr)File size",
"recordingStartedDate": "(fr)Recording started date",
"recordingFinishedDate": "(fr)Recording finished date",
"uploadDate": "(fr)Upload date",
"transcriptionStartedDate": "(fr)Transcription start date",
"transcriptionFinishedDate": "(fr)Transcription finished date",
"transcriptionist": "(fr)Transcriptionist",
"comment": "(fr)Comment",
"optionItem1": "(fr)OptionItem1",
"optionItem2": "(fr)OptionItem2",
"optionItem3": "(fr)OptionItem3",
"optionItem4": "(fr)OptionItem4",
"optionItem5": "(fr)OptionItem5",
"optionItem6": "(fr)OptionItem6",
"optionItem7": "(fr)OptionItem7",
"optionItem8": "(fr)OptionItem8",
"optionItem9": "(fr)OptionItem9",
"optionItem10": "(fr)OptionItem10",
"filter": "(fr)Filter",
"uploaded": "(fr)Uploaded",
"inProgress": "(fr)InProgress",
"finished": "(fr)Finished",
"pending": "(fr)Pending",
"backup": "(fr)Backup",
"playback": "(fr)Playback",
"fileProperty": "(fr)File Property",
"changeTranscriptionist": "(fr)Change Transcriptionist",
"deleteDictation": "(fr)Delete Dictation",
"selectedTranscriptionist": "(fr)Selected",
"poolTranscriptionist": "(fr)Pool"
"title": "Dictées",
"displayInfomation": "Informations sur l'affichage",
"jobNumber": "Numéro de tâche",
"status": "État ",
"priority": "Priorité",
"encryption": "Chiffrement",
"authorId": "Identifiant Auteur",
"workType": "Identifiant du Type de travail",
"fileName": "Nom de fichier",
"fileLength": "Longueur du fichier",
"fileSize": "Taille de fichier",
"recordingStartedDate": "Date de début de la dictée",
"recordingFinishedDate": "Date de fin de la dictée",
"uploadDate": "Date de téléchargement",
"transcriptionStartedDate": "Date de début de transcription",
"transcriptionFinishedDate": "Date de fin de transcription",
"transcriptionist": "Transcriptionniste",
"comment": "Commentaire",
"optionItem1": "Elément d'option 1",
"optionItem2": "Elément d'option 2",
"optionItem3": "Elément d'option 3",
"optionItem4": "Elément d'option 4",
"optionItem5": "Elément d'option 5",
"optionItem6": "Elément d'option 6",
"optionItem7": "Elément d'option 7",
"optionItem8": "Elément d'option 8",
"optionItem9": "Elément d'option 9",
"optionItem10": "Elément d'option 10",
"filter": "Filtre",
"uploaded": "Téléchargé",
"inProgress": "En cours",
"finished": "Terminé",
"pending": "Suspendu",
"backup": "Sauvegarde",
"playback": "Lecture",
"fileProperty": "Propriété de fichier",
"changeTranscriptionist": "Changer de transcriptionniste ",
"deleteDictation": "Supprimer la dictée",
"selectedTranscriptionist": "Transcriptionniste sélectionné",
"poolTranscriptionist": "Liste de transcriptionniste"
}
},
"cardLicenseIssuePopupPage": {
"label": {
"title": "(fr)License Card",
"subTitle": "(fr)Card License Creation",
"number": "(fr)Number of create licenses",
"createButton": "(fr)Create"
"title": "Carte de licence",
"subTitle": "Création d'une licence de carte",
"number": "Nombre de licences",
"createButton": "Créer"
},
"message": {
"createSuccess": "(fr)処理に成功しました。ファイルのダウンロードが完了するまでお待ちください。",
"createNumberIncorrectError": "(fr)Number of create licensesには1以上の数字を入力してください。"
"createSuccess": "Le traitement a réussi.",
"createNumberIncorrectError": "Entrez un nombre de 1 ou plus dans Nombre de licences."
}
},
"partnerLicensePage": {
"label": {
"cardLicenseButton": "(fr)License Card"
"cardLicenseButton": "Carte de licence"
}
},
"cardLicenseActivatePopupPage": {
"label": {
"title": "(fr)Activate License Key",
"keyNumber": "(fr)Key number",
"activateButton": "(fr)activate",
"keyNumberIncorrectError": "(fr)Key Numberには20桁の半角大文字英数字を入力してください。"
"title": "Activer la clé de licence",
"keyNumber": "Clé",
"activateButton": "Activer",
"keyNumberIncorrectError": "Veuillez saisir la clé alphanumérique de 20 caractères."
},
"message": {
"LicenseKeyNotExistError": "(fr)入力されたライセンスキーは存在しません。ライセンスキーを再度お確かめください。",
"LicenseKeyAlreadyActivatedError": "(fr)入力されたライセンスキーは、既に有効化されています。ライセンスキーを再度お確かめください。"
"LicenseKeyNotExistError": "La clé de licence que vous avez saisie n'existe pas. Veuillez vérifier à nouveau votre clé de licence.",
"LicenseKeyAlreadyActivatedError": "La clé de licence que vous avez saisie est déjà activée. Veuillez essayer d'utiliser une clé de licence différente."
}
},
"addPartnerAccountPopupPage": {
"message": {
"addAccountSuccess": "(fr)メールアドレス宛に認証用メールを送信しました。",
"inputEmptyError": "(fr)この項目の入力は必須です。入力してください。",
"emailIncorrectError": "(fr)メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
"emailConflictError": "(fr)このメールアドレスは既に登録されています。他のメールアドレスで登録してください。"
"addAccountSuccess": "Un e-mail de vérification a été envoyé à votre adresse e-mail.",
"inputEmptyError": "Champ obligatoire",
"emailIncorrectError": "Le format de l'adresse e-mail n'est pas valide. Veuillez saisir un format d'adresse e-mail valide. (Le système identifie les e-mails non valides, par exemple sans la marque ou la partie avec .com, cela ne semble pas correct.)",
"emailConflictError": "Cette adresse email est déjà enregistrée. Veuillez vous inscrire avec une autre adresse e-mail."
},
"text": {
"title": "(fr)Add account",
"accountInfoTitle": "(fr)Register account information",
"countryExplanation": "(fr)Please select country or the nearest country.",
"adminInfoTitle": "(fr)Register primary administrator's information"
"title": "Ajouter compte",
"accountInfoTitle": "Enregistrer les informations du compte",
"countryExplanation": "Sélectionnez le pays où vous vous trouvez. Si votre pays ne figure pas dans la liste, veuillez sélectionner le pays le plus proche.",
"adminInfoTitle": "Enregistrer les informations de l'administrateur principal"
},
"label": {
"company": "(fr)Company Name",
"country": "(fr)Country",
"adminName": "(fr)Admin Name",
"email": "(fr)Email",
"createAccountButton": "(fr)Add account"
"company": "Nom de l'entreprise",
"country": "Pays",
"adminName": "Nom de l'administrateur",
"email": "Email",
"createAccountButton": "Ajouter compte"
}
},
"partnerLicense": {
"label": {
"title": "(fr)License",
"subTitle": "(fr)License for partners",
"orderLicenseButton": "(fr)Order License",
"orderHistoryButton": "(fr)Order History",
"IssueLicenseCardButton": "(fr)License Card",
"displayCardHistoryButton": "(fr)Card History",
"name": "(fr)Name",
"category": "(fr)Category",
"accountId": "(fr)Account ID",
"stockLicense": "(fr)Stock license",
"issueRequested": "(fr)Issue Requested",
"shortage": "(fr)Shortage",
"issueRequesting": "(fr)Issue Requesting",
"viewDetails": "(fr)View details",
"accounts": "(fr)accounts"
"title": "Licence",
"subTitle": "Licences partenaires",
"orderLicenseButton": "Commander licence",
"orderHistoryButton": "Historique des commandes",
"IssueLicenseCardButton": "Carte de licence",
"displayCardHistoryButton": "Historique de la carte",
"name": "Nom de l'entreprise",
"category": "Niveau compte",
"accountId": "Identifiant Auteur",
"stockLicense": "Inventaire des licences",
"issueRequested": "Licences demandées",
"shortage": "Pénurie de licences",
"issueRequesting": "Licences en commande",
"viewDetails": "Voir les détails",
"accounts": "comptes"
}
},
"orderHistoriesPage": {
"label": {
"title": "(fr)License",
"orderHistory": "(fr)Order History",
"orderDate": "(fr)Order date",
"issueDate": "(fr)Issue date",
"numberOfOrder": "(fr)Number of Order",
"poNumber": "(fr)PO Number",
"status": "(fr)Status",
"issueRequesting": "(fr)Issue Requesting",
"issued": "(fr)Issued",
"orderCanceled": "(fr)Order Canceled",
"issue": "(fr)Issue",
"issueCancel": "(fr)Issue Cancel",
"orderCancel": "(fr)Order Cancel",
"histories": "(fr)histories"
"title": "Licence",
"orderHistory": "Historique des commandes",
"orderDate": "Date de commande",
"issueDate": "Date de émission",
"numberOfOrder": "Nombre de licences commandées",
"poNumber": "Numéro de bon de commande",
"status": "État",
"issueRequesting": "Licences en commande",
"issued": "Licence issued",
"orderCanceled": "Commande annulée",
"issue": "Problème",
"issueCancel": "Annuler les licences",
"orderCancel": "Annuler commande",
"histories": "histoires"
},
"message": {
"notEnoughOfNumberOfLicense": "(fr)ライセンスが不足しているため、発行することができませんでした。ライセンスの注文を行ってください。",
"alreadyIssueLicense": "(fr)すでに発行済みの注文です。画面を更新してください。",
"alreadyLicenseIssueOrCancel": "(fr)ライセンス注文のキャンセルに失敗しました。選択された注文はすでに発行またはキャンセルされています。画面を更新して再度ご確認ください。",
"alreadyLicenseStatusChanged": "(fr)ライセンス発行のキャンセルに失敗しました。選択された注文の状態が変更されています。画面を更新して再度ご確認ください。",
"expiredSinceIssued": "(fr)発行日から一定期間経過しているため、発行キャンセルできません。",
"alreadyLicenseAllocated": "(fr)発行したライセンスがすでに割り当てられたため、発行キャンセルできません。"
"notEnoughOfNumberOfLicense": "Les licences n'ont pas pu être délivrées en raison d'un nombre insuffisant de licences. Veuillez commander des licences supplémentaires.",
"alreadyIssueLicense": "La commande a déjà été passée. Les informations affichées peuvent être obsolètes, veuillez donc actualiser l'écran pour voir le dernier statut.",
"alreadyLicenseIssueOrCancel": "Échec de l'annulation de la commande de licence. La licence pour la commande sélectionnée a déjà été délivrée ou la commande a déjà été annulée. Veuillez actualiser l'écran et vérifier à nouveau.",
"alreadyLicenseStatusChanged": "Échec de l'annulation de la délivrance de la licence. Le statut de la commande sélectionnée a changé. Veuillez actualiser l'écran et vérifier à nouveau.",
"expiredSinceIssued": "Les licences délivrées ne peuvent pas être annulées car plus de 14 jours se sont écoulés depuis la date de délivrance.",
"alreadyLicenseAllocated": "La licence délivrée ne peut pas être annulée car elle a déjà été attribuée."
}
},
"allocateLicensePopupPage": {
"label": {
"title": "(fr)License Allocation",
"personalInformation": "(fr)Personal Information",
"email": "(fr)Email",
"name": "(fr)Name",
"authorID": "(fr)Author ID",
"status": "(fr)Status",
"allocated": "(fr)Allocated",
"notAllocated": "(fr)Not Allocated",
"expirationDate": "(fr)Expiration date",
"licenseInformation": "(fr)License Information",
"licenseAvailable": "(fr)License available",
"dropDownHeading": "(fr)Select a license.",
"oneYear": "(fr)One Year",
"allocateLicense": "(fr)OK"
"title": "Attribuer une licence",
"email": "Email",
"name": "Nom",
"authorID": "Identifiant Auteur",
"status": "État",
"allocated": "Licence attribuée",
"notAllocated": "Licence non attribuée",
"expirationDate": "Date d'expiration",
"licenseInformation": "Informations sur la licence",
"licenseAvailable": "Licence disponible",
"dropDownHeading": "Sélectionnez la licence",
"oneYear": "Un ans",
"allocateLicense": "OK"
},
"message": {
"inputEmptyError": "(fr)この項目の入力は必須です。入力してください。",
"licenseAllocationFailure": "(fr)ライセンスの割り当てに失敗しました。他のライセンスを選択して再度割り当てをしてください。"
"inputEmptyError": "Champ obligatoire",
"licenseAllocationFailure": "Échec de l'attribution de la licence. Les informations affichées peuvent être obsolètes, veuillez donc actualiser l'écran pour voir le dernier statut."
}
},
"workflowPage": {
"label": {
"title": "(fr)Workflow"
"title": "Flux de travail"
}
},
"typistGroupSetting": {
"label": {
"title": "(fr)Transctiprionist Group",
"addGroup": "(fr)Add Group",
"groupName": "(fr)Group Name",
"addTypistGroup": "(fr)Add Transcriptionist Group",
"transcriptionist": "(fr)Transcriptionist",
"selected": "(fr)Selected",
"pool": "(fr)Pool",
"add": "(fr)Add",
"remove": "(fr)Remove",
"editTypistGroup": "(fr)Edit Transcriptionist Group"
"title": "Groupe de transcriptionniste",
"addGroup": "Ajouter groupe",
"groupName": "Nom de groupe",
"addTypistGroup": "Ajouter un groupe de transcripteurs",
"transcriptionist": "Transcriptionniste",
"selected": "Transcriptionniste sélectionné",
"pool": "Liste de transcriptionniste",
"add": "Ajouter",
"remove": "Supprimer",
"editTypistGroup": "Modifier le groupe de transcripteurs"
},
"message": {
"selectedTypistEmptyError": "(fr)TranscriptionistがいないTranscriptionistGroupは保存できません。1名以上をTranscriptionistとして選択してください。",
"groupSaveFailedError": "(fr)TypistGroupの保存に失敗しました。画面を更新し、再度実行してください"
"selectedTypistEmptyError": "Un ou plusieurs transcripteurs doivent être sélectionnés pour enregistrer un groupe de transcription.",
"groupSaveFailedError": "Le groupe de dactylographes n'a pas pu être enregistré. Les informations affichées peuvent être obsolètes, veuillez donc actualiser l'écran pour voir le dernier statut."
}
},
"worktypeIdSetting": {
"label": {
"title": "(fr)Worktype ID Setting",
"activeWorktypeId": "(fr)Active Worktype ID",
"addWorktypeId": "(fr)Add Worktype ID",
"worktypeId": "(fr)Worktype ID",
"description": "(fr)Description",
"descriptionOptional": "(fr)Description (Optional)",
"optionItem": "(fr)Option Item",
"worktypeIdTerms": "(fr)WorktypeID should be alphanumeric and symbols,\nbut not include: \\ / : * ? “ < > | .",
"addWorktype": "(fr)Add Worktype",
"editWorktypeId": "(fr)Edit Worktype ID",
"saveChange": "(fr)Save Changes",
"title": "Paramètre d'ID du type de travail",
"activeWorktypeId": "ID du type de travail actif",
"addWorktypeId": "Ajouter un ID de type de travail",
"worktypeId": "Identifiant du Type de travail",
"description": "Description",
"descriptionOptional": "Description (Facultatif)",
"optionItem": "Elément doption",
"worktypeIdTerms": "L'ID du type de travail peut contenir des caractères alphanumériques et des symboles. Les symboles suivants ne peuvent pas être utilisés :\\/:*?\"<>|.",
"addWorktype": "Ajouter type de travail",
"editWorktypeId": "Modifier l'ID du type de travail",
"saveChange": "Sauvegarder les modifications",
"editOptionItems": "(fr)Option Item",
"itemLabel": "(fr)Item label",
"defaultValue": "(fr)Default value",
@ -404,28 +400,40 @@
"optionItemTerms": "(fr)The Item label and Initial value should be alphanumeric and symbols, but not include: \\ / : * ? “ < > | ."
},
"message": {
"worktypeIdIncorrectError": "(fr)入力されたWorktypeIDがルールを満たしていません。下記のルールを満たすWorktypeIDを入力してください",
"alreadyWorktypeIdExistError": "(fr)このWorktype IDは既に登録されています。他のWorktype IDで登録してください。",
"worktypeIDLimitError": "(fr)Worktype IDが登録件数の上限に達しているため追加できません。",
"worktypeIdIncorrectError": "L'ID du type de travail que vous avez saisi ne répond pas aux spécifications. Veuillez saisir le type de travail correct, comme indiqué dans les spécifications ci-dessous.",
"alreadyWorktypeIdExistError": "Cet ID de type de travail est actuellement enregistré. Veuillez vous inscrire avec un identifiant de type de travail différent.",
"worktypeIDLimitError": "L'ID du type de travail ne peut pas être ajouté car il a atteint le nombre maximum d'enregistrements.",
"optionItemInvalidError": "(fr)Default valueがDefaultに設定されている場合、Initial valueは入力が必須です。",
"optionItemSaveFailedError": "(fr)オプションアイテムの保存に失敗しました。画面を更新し、再度実行してください",
"optionItemIncorrectError": "(fr)入力されたItem labelまたはInitial valueがルールを満たしていません。下記のルールを満たす値を入力してください",
"updateActiveWorktypeFailedError": "(fr)Active WorktypeIDの保存に失敗しました。画面を更新し、再度実行してください"
}
},
"templateFilePage": {
"label": {
"title": "(fr)Template List",
"addTemplate": "(fr)Add Template",
"fileName": "(fr)Flie Name",
"chooseFile": "(fr)Choose File",
"notFileChosen": "(fr)- Not file chosen -",
"fileSizeTerms": "(fr)ファイルサイズは5MBまでです。",
"fileSizeError": "(fr)選択されたファイルのサイズが大きすぎます。サイズが5MB以下のファイルを選択してください。",
"fileEmptyError": "(fr)ファイル選択は必須です。ファイルを選択してください。"
}
},
"partnerPage": {
"label": {
"title": "(fr)Partners",
"addAccount": "(fr)Add Account",
"name": "(fr)Name",
"category": "(fr)Category",
"accountId": "(fr)Account ID",
"country": "(fr)Country",
"primaryAdmin": "(fr)Primary Admin",
"email": "(fr)E-mail",
"dealerManagement": "(fr)Dealer Management",
"partners": "(fr)partners",
"deleteAccount": "(fr)Delete Account"
"title": "Partenaires",
"addAccount": "Ajouter compte",
"name": "Nom de l'entreprise",
"category": "Niveau compte",
"accountId": "Identifiant Auteur",
"country": "Pays",
"primaryAdmin": "Administrateur principal",
"email": "Email",
"dealerManagement": "Autoriser le revendeur à modifier les paramètres",
"partners": "Partenaires",
"deleteAccount": "Supprimer le compte"
}
},
"accountPage": {
@ -451,5 +459,14 @@
"message": {
"updateAccountFailedError": "(fr)アカウント情報の保存に失敗しました。画面を更新し、再度実行してください"
}
},
"deleteAccountPopup": {
"label": {
"title": "(fr)Delete Account",
"subTitle": "(fr)Delete your account",
"cautionOfDeleteingAccountData": "(fr)Deleting your account will remove all of your audio files and\nlicenses from system. and you'll cannot use ODMS Cloud.\nThis cannot be undone.",
"deleteButton": "(fr)Delete account",
"cancelButton": "(fr)Cancel"
}
}
}

View File

@ -16,4 +16,9 @@ export default defineConfig({
minify: false,
},
plugins: [env(), tsconfigPaths(), react(), sassDts()],
resolve: {
alias: {
os: "rollup-plugin-node-polyfills/polyfills/os",
},
},
});

View File

@ -2,7 +2,6 @@ version: '3'
services:
dictation_server:
env_file: ../.env
build: .
working_dir: /app/dictation_server
ports:

View File

@ -8,6 +8,7 @@ AZURE_CLIENT_SECRET=xxxxxxxx
ADB2C_TENANT_ID=xxxxxxxx
ADB2C_CLIENT_ID=xxxxxxxx
ADB2C_CLIENT_SECRET=xxxxxxxx
ADB2C_ORIGIN=https://zzzzzzzzzz
KEY_VAULT_NAME=kv-odms-secret-dev
JWT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA5IZZNgDew9eGmuFTezwdHYLSaJvUPPIKYoiOeVLD1paWNI51\n7Vkaoh0ngprcKOdv6T1N07V4igK7mOim2zY3yCTR6wcWR3PfFJrl9vh5SOo79koZ\noJb27YiM4jtxfx2dezzp0T2GoNR5rRolPUbWFJXnDe0DVXYXpJLb4LAlF2XAyYX0\nSYKUVUsJnzm5k4xbXtnwPwVbpm0EdswBE6qSfiL9zWk9dvHoKzSnfSDzDFoFcEoV\nchawzYXf/MM1YR4wo5XyzECc6Q5Ah4z522//mBNNaDHv83Yuw3mGShT73iJ0JQdk\nTturshv2Ecma38r6ftrIwNYXw4VVatJM8+GOOQIDAQABAoIBADrwp7u097+dK/tw\nWD61n3DIGAqg/lmFt8X4IH8MKLSE/FKr16CS1bqwOEuIM3ZdUtDeXd9Xs7IsyEPE\n5ZwuXK7DSF0M4+Mj8Ip49Q0Aww9aUoLQU9HGfgN/r4599GTrt31clZXA/6Mlighq\ncOZgCcEfdItz8OMu5SQuOIW4CKkCuaWnPOP26UqZocaXNZfpZH0iFLATMMH/TT8x\nay9ToHTQYE17ijdQ/EOLSwoeDV1CU1CIE3P4YfLJjvpKptly5dTevriHEzBi70Jx\n/KEPUn9Jj2gZafrUxRVhmMbm1zkeYxL3gsqRuTzRjEeeILuZhSJyCkQZyUNARxsg\nQY4DZfECgYEA+YLKUtmYTx60FS6DJ4s31TAsXY8kwhq/lB9E3GBZKDd0DPayXEeK\n4UWRQDTT6MI6fedW69FOZJ5sFLp8HQpcssb4Weq9PCpDhNTx8MCbdH3Um5QR3vfW\naKq/1XM8MDUnx5XcNYd87Aw3azvJAvOPr69as8IPnj6sKaRR9uQjbYUCgYEA6nfV\n5j0qmn0EJXZJblk4mvvjLLoWSs17j9YlrZJlJxXMDFRYtgnelv73xMxOMvcGoxn5\nifs7dpaM2x5EmA6jVU5sYaB/beZGEPWqPYGyjIwXPvUGAAv8Gbnvpp+xlSco/Dum\nIq0w+43ry5/xWh6CjfrvKV0J2bDOiJwPEdu/8iUCgYEAnBBSvL+dpN9vhFAzeOh7\nY71eAqcmNsLEUcG9MJqTKbSFwhYMOewF0iHRWHeylEPokhfBJn8kqYrtz4lVWFTC\n5o/Nh3BsLNXCpbMMIapXkeWiti1HgE9ErPMgSkJpwz18RDpYIqM8X+jEQS6D7HSr\nyxfDg+w+GJza0rEVE3hfMIECgYBw+KZ2VfhmEWBjEHhXE+QjQMR3s320MwebCUqE\nNCpKx8TWF/naVC0MwfLtvqbbBY0MHyLN6d//xpA9r3rLbRojqzKrY2KiuDYAS+3n\nzssRzxoQOozWju+8EYu30/ADdqfXyIHG6X3VZs87AGiQzGyJLmP3oR1y5y7MQa09\nJI16hQKBgHK5uwJhGa281Oo5/FwQ3uYLymbNwSGrsOJXiEu2XwJEXwVi2ELOKh4/\n03pBk3Kva3fIwEK+vCzDNnxShIQqBE76/2I1K1whOfoUehhYvKHGaXl2j70Zz9Ks\nrkGW1cx7p+yDqATDrwHBHTHFh5bUTTn8dN40n0e0W/llurpbBkJM\n-----END RSA PRIVATE KEY-----\n"
JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5IZZNgDew9eGmuFTezwd\nHYLSaJvUPPIKYoiOeVLD1paWNI517Vkaoh0ngprcKOdv6T1N07V4igK7mOim2zY3\nyCTR6wcWR3PfFJrl9vh5SOo79koZoJb27YiM4jtxfx2dezzp0T2GoNR5rRolPUbW\nFJXnDe0DVXYXpJLb4LAlF2XAyYX0SYKUVUsJnzm5k4xbXtnwPwVbpm0EdswBE6qS\nfiL9zWk9dvHoKzSnfSDzDFoFcEoVchawzYXf/MM1YR4wo5XyzECc6Q5Ah4z522//\nmBNNaDHv83Yuw3mGShT73iJ0JQdkTturshv2Ecma38r6ftrIwNYXw4VVatJM8+GO\nOQIDAQAB\n-----END PUBLIC KEY-----\n"

View File

@ -0,0 +1,5 @@
-- +migrate Up
ALTER TABLE `license_allocation_history` ADD COLUMN `account_id` BIGINT UNSIGNED NOT NULL COMMENT 'アカウントID' AFTER `is_allocated`;
-- +migrate Down
ALTER TABLE `license_allocation_history` DROP COLUMN `account_id`;

View File

@ -0,0 +1,23 @@
-- +migrate Up
CREATE TABLE IF NOT EXISTS `users_archive` (
`id` BIGINT UNSIGNED NOT NULL PRIMARY KEY COMMENT 'ID',
`external_id` VARCHAR(255) NOT NULL COMMENT '外部ユーザーID',
`account_id` BIGINT UNSIGNED COMMENT 'アカウントID',
`role` VARCHAR(255) NOT NULL COMMENT '役職',
`author_id` VARCHAR(255) COMMENT 'AuthorID',
`accepted_terms_version` VARCHAR(255) NOT NULL COMMENT '同意済み利用規約バージョン',
`email_verified` BOOLEAN NOT NULL DEFAULT 0 COMMENT 'email認証が完了済みであるか',
`encryption` BOOLEAN DEFAULT FALSE NOT NULL COMMENT '音声ファイル暗号化するか',
`prompt` BOOLEAN DEFAULT FALSE NOT NULL COMMENT '録音時に強制的にWorkTypeIDの選択画面に遷移するか',
`deleted_at` TIMESTAMP COMMENT '削除時刻',
`created_by` VARCHAR(255) COMMENT '作成者',
`created_at` TIMESTAMP DEFAULT now() COMMENT '作成時刻',
`updated_by` VARCHAR(255) COMMENT '更新者',
`updated_at` TIMESTAMP DEFAULT now() COMMENT '更新時刻',
`auto_renew` BOOLEAN DEFAULT TRUE NOT NULL COMMENT 'ライセンスの自動更新をするかどうか',
`license_alert` BOOLEAN DEFAULT TRUE NOT NULL COMMENT 'ライセンスの期限切れ通知をするかどうか',
`notification` BOOLEAN DEFAULT TRUE NOT NULL COMMENT '完了通知をするかどうか'
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
-- +migrate Down
DROP TABLE `users_archive`;

View File

@ -0,0 +1,20 @@
-- +migrate Up
CREATE TABLE IF NOT EXISTS `licenses_archive` (
`id` BIGINT UNSIGNED NOT NULL PRIMARY KEY COMMENT 'ライセンスID',
`expiry_date` TIMESTAMP COMMENT '有効期限',
`account_id` BIGINT UNSIGNED NOT NULL COMMENT '所有アカウントID',
`type` VARCHAR(255) NOT NULL COMMENT 'ライセンス種別(トライアル/通常/カード)',
`status` VARCHAR(255) NOT NULL COMMENT 'ライセンス状態(未割当/割り当て済/削除済)',
`allocated_user_id` BIGINT UNSIGNED COMMENT '割り当てユーザID',
`order_id` BIGINT UNSIGNED COMMENT '注文ID',
`deleted_at` TIMESTAMP COMMENT '削除日時(オリジナルテーブル)',
`created_at` TIMESTAMP COMMENT '作成時刻(オリジナルテーブル)',
`created_by` VARCHAR(255) COMMENT '作成者(オリジナルテーブル)',
`updated_at` TIMESTAMP COMMENT '更新時刻(オリジナルテーブル)',
`updated_by` VARCHAR(255) COMMENT '更新者(オリジナルテーブル)',
`delete_order_id` BIGINT UNSIGNED COMMENT '削除注文ID',
`archived_at` TIMESTAMP DEFAULT now() COMMENT '退避日時'
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
-- +migrate Down
DROP TABLE `licenses_archive`;

View File

@ -0,0 +1,19 @@
-- +migrate Up
CREATE TABLE IF NOT EXISTS `license_allocation_history_archive` (
`id` BIGINT UNSIGNED NOT NULL PRIMARY KEY COMMENT '割り当て履歴ID',
`user_id` BIGINT UNSIGNED NOT NULL COMMENT 'ユーザーID',
`license_id` BIGINT UNSIGNED NOT NULL COMMENT 'ライセンスID',
`is_allocated` BOOLEAN NOT NULL DEFAULT 0 COMMENT '割り当て済みか',
`account_id` BIGINT UNSIGNED NOT NULL COMMENT 'アカウントID',
`executed_at` TIMESTAMP NOT NULL COMMENT '実施日時',
`switch_from_type` VARCHAR(255) NOT NULL COMMENT '切り替え元種別(特になし/カード/トライアル)',
`deleted_at` TIMESTAMP COMMENT '削除時刻(オリジナルテーブル)',
`created_by` VARCHAR(255) COMMENT '作成者(オリジナルテーブル)',
`created_at` TIMESTAMP COMMENT '作成時刻(オリジナルテーブル)',
`updated_by` VARCHAR(255) COMMENT '更新者(オリジナルテーブル)',
`updated_at` TIMESTAMP COMMENT '更新時刻(オリジナルテーブル)',
`archived_at` TIMESTAMP DEFAULT now() COMMENT '退避日時'
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
-- +migrate Down
DROP TABLE `license_allocation_history_archive`;

View File

@ -0,0 +1,31 @@
-- +migrate Up
ALTER TABLE `users_archive`
MODIFY COLUMN `accepted_terms_version` VARCHAR(255) COMMENT '同意済み利用規約バージョン',
MODIFY COLUMN `email_verified` BOOLEAN NOT NULL COMMENT 'email認証が完了済みであるか',
MODIFY COLUMN `encryption` BOOLEAN NOT NULL COMMENT '音声ファイル暗号化するか',
MODIFY COLUMN `prompt` BOOLEAN NOT NULL COMMENT '録音時に強制的にWorkTypeIDの選択画面に遷移するか',
MODIFY COLUMN `deleted_at` TIMESTAMP COMMENT '削除時刻(オリジナルテーブル)',
MODIFY COLUMN `created_at` TIMESTAMP COMMENT '作成時刻(オリジナルテーブル)',
MODIFY COLUMN `created_by` VARCHAR(255) COMMENT '作成者(オリジナルテーブル)',
MODIFY COLUMN `updated_at` TIMESTAMP COMMENT '更新時刻(オリジナルテーブル)',
MODIFY COLUMN `updated_by` VARCHAR(255) COMMENT '更新者(オリジナルテーブル)',
MODIFY COLUMN `auto_renew` BOOLEAN NOT NULL COMMENT 'ライセンスの自動更新をするかどうか',
MODIFY COLUMN `license_alert` BOOLEAN NOT NULL COMMENT 'ライセンスの期限切れ通知をするかどうか',
MODIFY COLUMN `notification` BOOLEAN NOT NULL COMMENT '完了通知をするかどうか',
ADD COLUMN `archived_at` TIMESTAMP DEFAULT now() COMMENT '退避日時' AFTER `notification`;
-- +migrate Down
ALTER TABLE `users_archive`
MODIFY COLUMN `accepted_terms_version` VARCHAR(255) NOT NULL COMMENT '同意済み利用規約バージョン',
MODIFY COLUMN `email_verified` BOOLEAN NOT NULL DEFAULT 0 COMMENT 'email認証が完了済みであるか',
MODIFY COLUMN `encryption` BOOLEAN DEFAULT FALSE NOT NULL COMMENT '音声ファイル暗号化するか',
MODIFY COLUMN `prompt` BOOLEAN DEFAULT FALSE NOT NULL COMMENT '録音時に強制的にWorkTypeIDの選択画面に遷移するか',
MODIFY COLUMN `deleted_at` TIMESTAMP COMMENT '削除時刻',
MODIFY COLUMN `created_at` TIMESTAMP DEFAULT now() COMMENT '作成時刻',
MODIFY COLUMN `created_by` VARCHAR(255) COMMENT '作成者',
MODIFY COLUMN `updated_at` TIMESTAMP DEFAULT now() COMMENT '更新時刻',
MODIFY COLUMN `updated_by` VARCHAR(255) COMMENT '更新者',
MODIFY COLUMN `auto_renew` BOOLEAN DEFAULT TRUE NOT NULL COMMENT 'ライセンスの自動更新をするかどうか',
MODIFY COLUMN `license_alert` BOOLEAN DEFAULT TRUE NOT NULL COMMENT 'ライセンスの期限切れ通知をするかどうか',
MODIFY COLUMN `notification` BOOLEAN DEFAULT TRUE NOT NULL COMMENT '完了通知をするかどうか',
DROP COLUMN `archived_at`;

View File

@ -279,6 +279,42 @@
"security": [{ "bearer": [] }]
}
},
"/accounts/authors": {
"get": {
"operationId": "getAuthors",
"summary": "",
"description": "ログインしているユーザーのアカウント配下のAuthor一覧を取得します",
"parameters": [],
"responses": {
"200": {
"description": "成功時のレスポンス",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/GetAuthorsResponse" }
}
}
},
"401": {
"description": "認証エラー",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
}
}
},
"500": {
"description": "想定外のサーバーエラー",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
}
}
}
},
"tags": ["accounts"],
"security": [{ "bearer": [] }]
}
},
"/accounts/typists": {
"get": {
"operationId": "getTypists",
@ -2848,6 +2884,98 @@
"security": [{ "bearer": [] }]
}
},
"/workflows": {
"get": {
"operationId": "getWorkflows",
"summary": "",
"description": "アカウント内のワークフローの一覧を取得します",
"parameters": [],
"responses": {
"200": {
"description": "成功時のレスポンス",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/GetWorkflowsResponse"
}
}
}
},
"401": {
"description": "認証エラー",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
}
}
},
"500": {
"description": "想定外のサーバーエラー",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
}
}
}
},
"tags": ["workflows"],
"security": [{ "bearer": [] }]
},
"post": {
"operationId": "createWorkflows",
"summary": "",
"description": "アカウント内にワークフローを新規作成します",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateWorkflowsRequest"
}
}
}
},
"responses": {
"200": {
"description": "成功時のレスポンス",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateWorkflowsResponse"
}
}
}
},
"400": {
"description": "パラメータ不正エラー",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
}
}
},
"401": {
"description": "認証エラー",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
}
}
},
"500": {
"description": "想定外のサーバーエラー",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
}
}
}
},
"tags": ["workflows"],
"security": [{ "bearer": [] }]
}
},
"/notification/register": {
"post": {
"operationId": "register",
@ -3036,6 +3164,24 @@
"properties": { "account": { "$ref": "#/components/schemas/Account" } },
"required": ["account"]
},
"Author": {
"type": "object",
"properties": {
"id": { "type": "number", "description": "Authorユーザーの内部ID" },
"authorId": { "type": "string", "description": "AuthorID" }
},
"required": ["id", "authorId"]
},
"GetAuthorsResponse": {
"type": "object",
"properties": {
"authors": {
"type": "array",
"items": { "$ref": "#/components/schemas/Author" }
}
},
"required": ["authors"]
},
"Typist": {
"type": "object",
"properties": {
@ -4035,6 +4181,92 @@
},
"required": ["templates"]
},
"WorkflowWorktype": {
"type": "object",
"properties": {
"id": { "type": "number", "description": "Worktypeの内部ID" },
"worktypeId": { "type": "string", "description": "WorktypeID" }
},
"required": ["id", "worktypeId"]
},
"WorkflowTemplate": {
"type": "object",
"properties": {
"id": { "type": "number", "description": "テンプレートの内部ID" },
"fileName": {
"type": "string",
"description": "テンプレートのファイル名"
}
},
"required": ["id", "fileName"]
},
"Workflow": {
"type": "object",
"properties": {
"id": { "type": "number", "description": "ワークフローの内部ID" },
"author": {
"description": "Author情報",
"allOf": [{ "$ref": "#/components/schemas/Author" }]
},
"worktype": {
"description": "Worktype情報",
"allOf": [{ "$ref": "#/components/schemas/WorkflowWorktype" }]
},
"template": {
"description": "テンプレート情報",
"allOf": [{ "$ref": "#/components/schemas/WorkflowTemplate" }]
},
"typists": {
"description": "ルーティング候補のタイピストユーザー/タイピストグループ",
"type": "array",
"items": { "$ref": "#/components/schemas/Assignee" }
}
},
"required": ["id", "author", "typists"]
},
"GetWorkflowsResponse": {
"type": "object",
"properties": {
"workflows": {
"description": "ワークフローの一覧",
"type": "array",
"items": { "$ref": "#/components/schemas/Workflow" }
}
},
"required": ["workflows"]
},
"WorkflowTypist": {
"type": "object",
"properties": {
"typistId": {
"type": "number",
"description": "タイピストユーザーの内部ID"
},
"typistGroupId": {
"type": "number",
"description": "タイピストグループの内部ID"
}
}
},
"CreateWorkflowsRequest": {
"type": "object",
"properties": {
"authorId": { "type": "number", "description": "Authornの内部ID" },
"worktypeId": { "type": "number", "description": "Worktypeの内部ID" },
"templateId": {
"type": "number",
"description": "テンプレートの内部ID"
},
"typists": {
"description": "ルーティング候補のタイピストユーザー/タイピストグループ",
"minItems": 1,
"type": "array",
"items": { "$ref": "#/components/schemas/WorkflowTypist" }
}
},
"required": ["authorId", "typists"]
},
"CreateWorkflowsResponse": { "type": "object", "properties": {} },
"RegisterRequest": {
"type": "object",
"properties": {

View File

@ -44,6 +44,10 @@ import { SortCriteriaRepositoryModule } from './repositories/sort_criteria/sort_
import { TemplateFilesRepositoryModule } from './repositories/template_files/template_files.repository.module';
import { WorktypesRepositoryModule } from './repositories/worktypes/worktypes.repository.module';
import { TemplatesService } from './features/templates/templates.service';
import { WorkflowsModule } from './features/workflows/workflows.module';
import { WorkflowsController } from './features/workflows/workflows.controller';
import { WorkflowsService } from './features/workflows/workflows.service';
import { validate } from './common/validators/env.validator';
@Module({
imports: [
@ -60,6 +64,7 @@ import { TemplatesService } from './features/templates/templates.service';
ConfigModule.forRoot({
envFilePath: ['.env.local', '.env'],
isGlobal: true,
validate,
}),
AuthModule,
AdB2cModule,
@ -71,6 +76,7 @@ import { TemplatesService } from './features/templates/templates.service';
TemplatesModule,
SendGridModule,
LicensesModule,
WorkflowsModule,
AccountsRepositoryModule,
UsersRepositoryModule,
LicensesRepositoryModule,
@ -111,6 +117,7 @@ import { TemplatesService } from './features/templates/templates.service';
UsersController,
LicensesController,
TemplatesController,
WorkflowsController,
],
providers: [
AuthService,
@ -121,6 +128,7 @@ import { TemplatesService } from './features/templates/templates.service';
TasksService,
LicensesService,
TemplatesService,
WorkflowsService,
],
})
export class AppModule {

View File

@ -32,6 +32,8 @@ import { LicensesService } from '../../features/licenses/licenses.service';
import { TasksService } from '../../features/tasks/tasks.service';
import { TemplatesService } from '../../features/templates/templates.service';
import { TemplatesModule } from '../../features/templates/templates.module';
import { WorkflowsService } from '../../features/workflows/workflows.service';
import { WorkflowsModule } from '../../features/workflows/workflows.module';
export const makeTestingModule = async (
datasource: DataSource,
@ -53,6 +55,7 @@ export const makeTestingModule = async (
SendGridModule,
LicensesModule,
TemplatesModule,
WorkflowsModule,
AccountsRepositoryModule,
UsersRepositoryModule,
LicensesRepositoryModule,
@ -78,6 +81,7 @@ export const makeTestingModule = async (
TasksService,
LicensesService,
TemplatesService,
WorkflowsService,
],
})
.useMocker(async (token) => {

View File

@ -173,6 +173,16 @@ export const overrideBlobstorageService = <TService>(
accountId: number,
country: string,
) => Promise<void>;
containerExists?: (
context: Context,
accountId: number,
country: string,
) => Promise<boolean>;
publishTemplateUploadSas?: (
context: Context,
accountId: number,
country: string,
) => Promise<string>;
},
): void => {
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
@ -189,6 +199,18 @@ export const overrideBlobstorageService = <TService>(
writable: true,
});
}
if (overrides.containerExists) {
Object.defineProperty(obj, obj.containerExists.name, {
value: overrides.containerExists,
writable: true,
});
}
if (overrides.publishTemplateUploadSas) {
Object.defineProperty(obj, obj.publishTemplateUploadSas.name, {
value: overrides.publishTemplateUploadSas,
writable: true,
});
}
};
/**

View File

@ -0,0 +1,198 @@
import { plainToClass } from 'class-transformer';
import {
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
validateSync,
} from 'class-validator';
/**
*
*/
export class EnvValidator {
// .env
@IsNotEmpty()
@IsString()
DB_HOST: string;
@IsNotEmpty()
@IsNumber()
DB_PORT: number;
@IsNotEmpty()
@IsNumber()
DB_EXTERNAL_PORT: number;
@IsNotEmpty()
@IsString()
DB_NAME: string;
@IsNotEmpty()
@IsString()
DB_ROOT_PASS: string;
@IsNotEmpty()
@IsString()
DB_USERNAME: string;
@IsNotEmpty()
@IsString()
DB_PASSWORD: string;
@IsOptional()
@IsString()
NO_COLOR: string;
@IsNotEmpty()
@IsNumber()
ACCESS_TOKEN_LIFETIME_WEB: number;
@IsNotEmpty()
@IsNumber()
REFRESH_TOKEN_LIFETIME_WEB: number;
@IsNotEmpty()
@IsNumber()
REFRESH_TOKEN_LIFETIME_DEFAULT: number;
@IsNotEmpty()
@IsString()
TENANT_NAME: string;
@IsNotEmpty()
@IsString()
SIGNIN_FLOW_NAME: string;
@IsNotEmpty()
@IsNumber()
EMAIL_CONFIRM_LIFETIME: number;
@IsNotEmpty()
@IsString()
APP_DOMAIN: string;
@IsNotEmpty()
@IsNumber()
STORAGE_TOKEN_EXPIRE_TIME: number;
// .env.local
@IsOptional()
@IsString()
STAGE: string;
@IsOptional()
@IsString()
CORS: string;
@IsNotEmpty()
@IsNumber()
PORT: number;
@IsNotEmpty()
@IsString()
AZURE_TENANT_ID: string;
@IsNotEmpty()
@IsString()
AZURE_CLIENT_ID: string;
@IsNotEmpty()
@IsString()
AZURE_CLIENT_SECRET: string;
@IsNotEmpty()
@IsString()
ADB2C_TENANT_ID: string;
@IsNotEmpty()
@IsString()
ADB2C_CLIENT_ID: string;
@IsNotEmpty()
@IsString()
ADB2C_CLIENT_SECRET: string;
@IsNotEmpty()
@IsString()
ADB2C_ORIGIN: string;
@IsNotEmpty()
@IsString()
KEY_VAULT_NAME: string;
@IsNotEmpty()
@IsString()
JWT_PRIVATE_KEY: string;
@IsNotEmpty()
@IsString()
JWT_PUBLIC_KEY: string;
@IsNotEmpty()
@IsString()
SENDGRID_API_KEY: string;
@IsNotEmpty()
@IsString()
MAIL_FROM: string;
@IsNotEmpty()
@IsString()
NOTIFICATION_HUB_NAME: string;
@IsNotEmpty()
@IsString()
NOTIFICATION_HUB_CONNECT_STRING: string;
@IsNotEmpty()
@IsString()
STORAGE_ACCOUNT_NAME_US: string;
@IsNotEmpty()
@IsString()
STORAGE_ACCOUNT_NAME_AU: string;
@IsNotEmpty()
@IsString()
STORAGE_ACCOUNT_NAME_EU: string;
@IsNotEmpty()
@IsString()
STORAGE_ACCOUNT_KEY_US: string;
@IsNotEmpty()
@IsString()
STORAGE_ACCOUNT_KEY_AU: string;
@IsNotEmpty()
@IsString()
STORAGE_ACCOUNT_KEY_EU: string;
@IsNotEmpty()
@IsString()
STORAGE_ACCOUNT_ENDPOINT_US: string;
@IsNotEmpty()
@IsString()
STORAGE_ACCOUNT_ENDPOINT_AU: string;
@IsNotEmpty()
@IsString()
STORAGE_ACCOUNT_ENDPOINT_EU: string;
}
export function validate(config: Record<string, unknown>) {
const validatedConfig = plainToClass(EnvValidator, config, {
enableImplicitConversion: true,
});
const errors = validateSync(validatedConfig, {
skipMissingProperties: false,
});
if (errors.length > 0) {
throw new Error(errors.toString());
}
return validatedConfig;
}

View File

@ -62,6 +62,7 @@ import {
UpdateAccountInfoResponse,
DeleteAccountRequest,
DeleteAccountResponse,
GetAuthorsResponse,
} from './types/types';
import { USER_ROLES, ADMIN_ROLES, TIERS } from '../../constants';
import { AuthGuard } from '../../common/guards/auth/authguards';
@ -201,6 +202,40 @@ export class AccountsController {
return accountInfo;
}
@ApiResponse({
status: HttpStatus.OK,
type: GetAuthorsResponse,
description: '成功時のレスポンス',
})
@ApiResponse({
status: HttpStatus.UNAUTHORIZED,
description: '認証エラー',
type: ErrorResponse,
})
@ApiResponse({
status: HttpStatus.INTERNAL_SERVER_ERROR,
description: '想定外のサーバーエラー',
type: ErrorResponse,
})
@ApiOperation({
operationId: 'getAuthors',
description:
'ログインしているユーザーのアカウント配下のAuthor一覧を取得します',
})
@ApiBearerAuth()
@UseGuards(AuthGuard)
@UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] }))
@Get('authors')
async getAuthors(@Req() req: Request): Promise<GetAuthorsResponse> {
const accessToken = retrieveAuthorizationToken(req);
const { userId } = jwt.decode(accessToken, { json: true }) as AccessToken;
const context = makeContext(userId);
console.log(context.trackingId);
return { authors: [] };
}
@ApiResponse({
status: HttpStatus.OK,
type: GetTypistsResponse,

View File

@ -134,6 +134,18 @@ export class GetMyAccountResponse {
account: Account;
}
export class Author {
@ApiProperty({ description: 'Authorユーザーの内部ID' })
id: number;
@ApiProperty({ description: 'AuthorID' })
authorId: string;
}
export class GetAuthorsResponse {
@ApiProperty({ type: [Author] })
authors: Author[];
}
export class Typist {
@ApiProperty({
description: 'TypistのユーザーID',

View File

@ -290,13 +290,16 @@ export class FilesController {
@Req() req: Request,
): Promise<TemplateUploadLocationResponse> {
const token = retrieveAuthorizationToken(req);
const accessToken = jwt.decode(token, { json: true }) as AccessToken;
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
const context = makeContext(accessToken.userId);
const context = makeContext(userId);
console.log(context.trackingId);
const url = await this.filesService.publishTemplateFileUploadSas(
context,
userId,
);
return { url: '' };
return { url };
}
@ApiResponse({
@ -333,13 +336,10 @@ export class FilesController {
): Promise<TemplateUploadFinishedReqponse> {
const { name, url } = body;
const token = retrieveAuthorizationToken(req);
const accessToken = jwt.decode(token, { json: true }) as AccessToken;
const context = makeContext(accessToken.userId);
console.log(context.trackingId);
console.log(name);
console.log(url);
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
const context = makeContext(userId);
await this.filesService.templateUploadFinished(context, userId, url, name);
return {};
}
}

View File

@ -6,6 +6,7 @@ import { AudioFilesRepositoryModule } from '../../repositories/audio_files/audio
import { AudioOptionItemsRepositoryModule } from '../../repositories/audio_option_items/audio_option_items.repository.module';
import { TasksRepositoryModule } from '../../repositories/tasks/tasks.repository.module';
import { BlobstorageModule } from '../../gateways/blobstorage/blobstorage.module';
import { TemplateFilesRepositoryModule } from '../../repositories/template_files/template_files.repository.module';
@Module({
imports: [
@ -14,6 +15,7 @@ import { BlobstorageModule } from '../../gateways/blobstorage/blobstorage.module
AudioOptionItemsRepositoryModule,
TasksRepositoryModule,
BlobstorageModule,
TemplateFilesRepositoryModule,
],
providers: [FilesService],
controllers: [FilesController],

View File

@ -10,7 +10,18 @@ import { DataSource } from 'typeorm';
import { createTask, makeTestingModuleWithBlob } from './test/utility';
import { FilesService } from './files.service';
import { makeContext } from '../../common/log';
import { makeTestSimpleAccount, makeTestUser } from '../../common/test/utility';
import {
makeTestAccount,
makeTestSimpleAccount,
makeTestUser,
} from '../../common/test/utility';
import { makeTestingModule } from '../../common/test/modules';
import { overrideBlobstorageService } from '../../common/test/overrides';
import {
createTemplateFile,
getTemplateFiles,
} from '../templates/test/utility';
import { TemplateFilesRepositoryService } from '../../repositories/template_files/template_files.repository.service';
describe('音声ファイルアップロードURL取得', () => {
it('アップロードSASトークンが乗っているURLを返却する', async () => {
@ -779,6 +790,232 @@ describe('テンプレートファイルダウンロードURL取得', () => {
});
});
describe('publishTemplateFileUploadSas', () => {
let source: DataSource = null;
beforeEach(async () => {
source = new DataSource({
type: 'sqlite',
database: ':memory:',
logging: false,
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
});
return source.initialize();
});
afterEach(async () => {
await source.destroy();
source = null;
});
it('テンプレートファイルアップロードSASトークンが乗っているURLを取得できる', async () => {
const module = await makeTestingModule(source);
const service = module.get<FilesService>(FilesService);
// 第五階層のアカウント作成
const { account, admin } = await makeTestAccount(source, { tier: 5 });
const context = makeContext(admin.external_id);
const baseUrl = `https://saodmsusdev.blob.core.windows.net/account-${account.id}/Templates`;
//SASトークンを返却する
overrideBlobstorageService(service, {
containerExists: async () => true,
publishTemplateUploadSas: async () => `${baseUrl}?sas-token`,
});
const url = await service.publishTemplateFileUploadSas(
context,
admin.external_id,
);
expect(url).toBe(`${baseUrl}?sas-token`);
});
it('blobストレージにコンテナが存在しない場合はエラーとなる', async () => {
const module = await makeTestingModule(source);
const service = module.get<FilesService>(FilesService);
// 第五階層のアカウント作成
const { admin } = await makeTestAccount(source, { tier: 5 });
const context = makeContext(admin.external_id);
//Blobコンテナ存在チェックに失敗するようにする
overrideBlobstorageService(service, {
containerExists: async () => false,
publishTemplateUploadSas: async () => '',
});
try {
await service.publishTemplateFileUploadSas(context, admin.external_id);
} catch (e) {
if (e instanceof HttpException) {
expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR);
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
} else {
fail();
}
}
});
it('SASトークンの取得に失敗した場合はエラーとなる', async () => {
const module = await makeTestingModule(source);
const service = module.get<FilesService>(FilesService);
// 第五階層のアカウント作成
const { admin } = await makeTestAccount(source, { tier: 5 });
const context = makeContext(admin.external_id);
//BlobのSASトークン生成に失敗するようにする
overrideBlobstorageService(service, {
containerExists: async () => true,
publishTemplateUploadSas: async () => {
throw new Error('blob failed');
},
});
try {
await service.publishTemplateFileUploadSas(context, admin.external_id);
} catch (e) {
if (e instanceof HttpException) {
expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR);
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
} else {
fail();
}
}
});
});
describe('templateUploadFinished', () => {
let source: DataSource = null;
beforeEach(async () => {
source = new DataSource({
type: 'sqlite',
database: ':memory:',
logging: false,
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
});
return source.initialize();
});
afterEach(async () => {
await source.destroy();
source = null;
});
it('アップロード完了後のテンプレートファイル情報をDBに保存できる新規追加', async () => {
const module = await makeTestingModule(source);
const service = module.get<FilesService>(FilesService);
// 第五階層のアカウント作成
const { account, admin } = await makeTestAccount(source, { tier: 5 });
const context = makeContext(admin.external_id);
const fileName = 'test.docs';
const url = `https://blob.url/account-${account.id}/Templates`;
// 事前にDBを確認
{
const templates = await getTemplateFiles(source, account.id);
expect(templates.length).toBe(0);
}
await service.templateUploadFinished(
context,
admin.external_id,
url,
fileName,
);
//実行結果を確認
{
const templates = await getTemplateFiles(source, account.id);
expect(templates.length).toBe(1);
expect(templates[0].file_name).toBe(fileName);
expect(templates[0].url).toBe(url);
}
});
it('アップロード完了後のテンプレートファイル情報をDBに保存できる更新', async () => {
const module = await makeTestingModule(source);
const service = module.get<FilesService>(FilesService);
// 第五階層のアカウント作成
const { account, admin } = await makeTestAccount(source, { tier: 5 });
const context = makeContext(admin.external_id);
const fileName = 'test.docs';
const url = `https://blob.url/account-${account.id}/Templates`;
await createTemplateFile(source, account.id, fileName, url);
// 事前にDBを確認
{
const templates = await getTemplateFiles(source, account.id);
expect(templates.length).toBe(1);
expect(templates[0].file_name).toBe(fileName);
expect(templates[0].url).toBe(url);
}
const updateUrl = `https://blob.update.url/account-${account.id}/Templates`;
await service.templateUploadFinished(
context,
admin.external_id,
updateUrl,
fileName,
);
//実行結果を確認
{
const templates = await getTemplateFiles(source, account.id);
expect(templates.length).toBe(1);
expect(templates[0].file_name).toBe(fileName);
expect(templates[0].url).toBe(updateUrl);
}
});
it('DBへの保存に失敗した場合はエラーとなる', async () => {
const module = await makeTestingModule(source);
const service = module.get<FilesService>(FilesService);
// 第五階層のアカウント作成
const { account, admin } = await makeTestAccount(source, { tier: 5 });
const context = makeContext(admin.external_id);
const fileName = 'test.docs';
const url = `https://blob.url/account-${account.id}/Templates`;
// 事前にDBを確認
{
const templates = await getTemplateFiles(source, account.id);
expect(templates.length).toBe(0);
}
//DBアクセスに失敗するようにする
const templatesService = module.get<TemplateFilesRepositoryService>(
TemplateFilesRepositoryService,
);
templatesService.upsertTemplateFile = jest
.fn()
.mockRejectedValue('DB failed');
try {
await service.templateUploadFinished(
context,
admin.external_id,
url,
fileName,
);
} catch (e) {
if (e instanceof HttpException) {
expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
} else {
fail();
}
}
});
});
const optionItemList = [
{
optionItemLabel: 'label_01',

View File

@ -23,6 +23,7 @@ import {
TypistUserNotFoundError,
} from '../../repositories/tasks/errors/types';
import { Context } from '../../common/log';
import { TemplateFilesRepositoryService } from '../../repositories/template_files/template_files.repository.service';
@Injectable()
export class FilesService {
@ -31,6 +32,7 @@ export class FilesService {
private readonly usersRepository: UsersRepositoryService,
private readonly tasksRepository: TasksRepositoryService,
private readonly tasksRepositoryService: TasksRepositoryService,
private readonly templateFilesRepository: TemplateFilesRepositoryService,
private readonly blobStorageService: BlobstorageService,
) {}
@ -535,4 +537,101 @@ export class FilesService {
);
}
}
/**
* URLを取得する
* @param context
* @param externalId
* @returns template file upload sas
*/
async publishTemplateFileUploadSas(
context: Context,
externalId: string,
): Promise<string> {
this.logger.log(
`[IN] [${context.trackingId}] ${this.publishTemplateFileUploadSas.name} | params: { externalId: ${externalId} };`,
);
try {
const {
account: { id: accountId, country },
} = await this.usersRepository.findUserByExternalId(externalId);
// 国に応じたリージョンのBlobストレージにコンテナが存在するか確認
const isContainerExists = await this.blobStorageService.containerExists(
context,
accountId,
country,
);
if (!isContainerExists) {
throw new Error('container not found.');
}
// SASトークン発行
const url = await this.blobStorageService.publishTemplateUploadSas(
context,
accountId,
country,
);
return url;
} catch (e) {
this.logger.error(`error=${e}`);
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
);
} finally {
this.logger.log(
`[OUT] [${context.trackingId}] ${this.publishTemplateFileUploadSas.name}`,
);
}
}
/**
* DBにテンプレートファイル情報を登録する
* @param context
* @param externalId
* @param url
* @param fileName
* @returns upload finished
*/
async templateUploadFinished(
context: Context,
externalId: string,
url: string,
fileName: string,
): Promise<void> {
this.logger.log(
`[IN] [${context.trackingId}] ${this.templateUploadFinished.name} | params: { externalId: ${externalId}, url: ${url}, fileName: ${fileName} };`,
);
try {
// ユーザー取得
const { account_id: accountId } =
await this.usersRepository.findUserByExternalId(externalId);
// URLにSASトークンがついている場合は取り除く;
const urlObj = new URL(url);
urlObj.search = '';
const fileUrl = urlObj.toString();
this.logger.log(`Request URL: ${url}, Without param URL${fileUrl}`);
// テンプレートファイル情報をDBに登録
await this.templateFilesRepository.upsertTemplateFile(
accountId,
fileName,
fileUrl,
);
} catch (e) {
this.logger.error(`error=${e}`);
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
);
} finally {
this.logger.log(
`[OUT] [${context.trackingId}] ${this.templateUploadFinished.name}`,
);
}
}
}

View File

@ -5,6 +5,7 @@ import { UsersRepositoryService } from '../../../repositories/users/users.reposi
import { FilesService } from '../files.service';
import { TasksRepositoryService } from '../../../repositories/tasks/tasks.repository.service';
import { Task } from '../../../repositories/tasks/entity/task.entity';
import { TemplateFilesRepositoryService } from '../../../repositories/template_files/template_files.repository.service';
export type BlobstorageServiceMockValue = {
createContainer: void | Error;
@ -39,6 +40,8 @@ export const makeFilesServiceMock = async (
return makeUsersRepositoryMock(usersRepositoryMockValue);
case TasksRepositoryService:
return makeTasksRepositoryMock(tasksRepositoryMockValue);
case TemplateFilesRepositoryService:
return {};
}
})
.compile();

View File

@ -552,7 +552,14 @@ describe('ライセンス割り当て', () => {
null,
null,
);
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
await createLicenseAllocationHistory(
source,
1,
userId,
1,
accountId,
'NONE',
);
const service = module.get<UsersService>(UsersService);
@ -581,6 +588,9 @@ describe('ライセンス割り当て', () => {
expect(licenseAllocationHistory.licenseAllocationHistory.is_allocated).toBe(
true,
);
expect(licenseAllocationHistory.licenseAllocationHistory.account_id).toBe(
accountId,
);
});
it('再割り当て可能なライセンスに対して、ライセンス割り当てが完了する', async () => {
@ -607,7 +617,14 @@ describe('ライセンス割り当て', () => {
null,
null,
);
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
await createLicenseAllocationHistory(
source,
1,
userId,
1,
accountId,
'NONE',
);
const service = module.get<UsersService>(UsersService);
@ -630,6 +647,9 @@ describe('ライセンス割り当て', () => {
expect(licenseAllocationHistory.licenseAllocationHistory.is_allocated).toBe(
true,
);
expect(licenseAllocationHistory.licenseAllocationHistory.account_id).toBe(
accountId,
);
});
it('未割当のライセンスに対して、別のライセンスが割り当てられているユーザーの割り当てが完了する', async () => {
@ -668,7 +688,14 @@ describe('ライセンス割り当て', () => {
null,
null,
);
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
await createLicenseAllocationHistory(
source,
1,
userId,
1,
accountId,
'NONE',
);
const service = module.get<UsersService>(UsersService);
@ -695,6 +722,9 @@ describe('ライセンス割り当て', () => {
expect(licenseAllocationHistory.licenseAllocationHistory.is_allocated).toBe(
false,
);
expect(licenseAllocationHistory.licenseAllocationHistory.account_id).toBe(
accountId,
);
// 新たに割り当てたライセンスの状態確認
const result2 = await selectLicense(source, 2);
@ -717,6 +747,9 @@ describe('ライセンス割り当て', () => {
expect(
newlicenseAllocationHistory.licenseAllocationHistory.is_allocated,
).toBe(true);
expect(
newlicenseAllocationHistory.licenseAllocationHistory.account_id,
).toBe(accountId);
});
it('割り当て時にライセンス履歴テーブルへの登録が完了する元がNORMALのとき', async () => {
@ -755,7 +788,14 @@ describe('ライセンス割り当て', () => {
null,
null,
);
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
await createLicenseAllocationHistory(
source,
1,
userId,
1,
accountId,
'NONE',
);
const service = module.get<UsersService>(UsersService);
await service.allocateLicense(makeContext('trackingId'), userId, 2);
@ -806,7 +846,14 @@ describe('ライセンス割り当て', () => {
null,
null,
);
await createLicenseAllocationHistory(source, 1, userId, 1, 'CARD');
await createLicenseAllocationHistory(
source,
1,
userId,
1,
accountId,
'CARD',
);
const service = module.get<UsersService>(UsersService);
await service.allocateLicense(makeContext('trackingId'), userId, 2);
@ -857,7 +904,14 @@ describe('ライセンス割り当て', () => {
null,
null,
);
await createLicenseAllocationHistory(source, 1, userId, 1, 'TRIAL');
await createLicenseAllocationHistory(
source,
1,
userId,
1,
accountId,
'TRIAL',
);
const service = module.get<UsersService>(UsersService);
await service.allocateLicense(makeContext('trackingId'), userId, 2);
@ -1000,7 +1054,14 @@ describe('ライセンス割り当て解除', () => {
null,
null,
);
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
await createLicenseAllocationHistory(
source,
1,
userId,
1,
accountId,
'NONE',
);
const service = module.get<UsersService>(UsersService);
await service.deallocateLicense(makeContext('trackingId'), userId);
@ -1028,6 +1089,9 @@ describe('ライセンス割り当て解除', () => {
expect(licenseAllocationHistory.licenseAllocationHistory.is_allocated).toBe(
false,
);
expect(licenseAllocationHistory.licenseAllocationHistory.account_id).toBe(
accountId,
);
expect(
licenseAllocationHistory.licenseAllocationHistory.switch_from_type,
).toBe('NONE');
@ -1075,7 +1139,14 @@ describe('ライセンス割り当て解除', () => {
null,
null,
);
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
await createLicenseAllocationHistory(
source,
1,
userId,
1,
accountId,
'NONE',
);
const service = module.get<UsersService>(UsersService);
await expect(

View File

@ -78,6 +78,7 @@ export const createLicenseAllocationHistory = async (
historyId: number,
userId: number,
licenseId: number,
accountId: number,
type: string,
): Promise<void> => {
const { identifiers } = await datasource
@ -87,6 +88,7 @@ export const createLicenseAllocationHistory = async (
user_id: userId,
license_id: licenseId,
is_allocated: true,
account_id: accountId,
executed_at: new Date(),
switch_from_type: type,
deleted_at: null,

View File

@ -26,3 +26,15 @@ export const createTemplateFile = async (
return templateFile;
};
export const getTemplateFiles = async (
datasource: DataSource,
accountId: number,
): Promise<TemplateFile[]> => {
const templates = await datasource.getRepository(TemplateFile).find({
where: {
account_id: accountId,
},
});
return templates;
};

View File

@ -892,7 +892,14 @@ export class UsersService {
);
try {
await this.licensesRepository.allocateLicense(userId, newLicenseId);
const accountId = (await this.usersRepository.findUserById(userId))
.account_id;
await this.licensesRepository.allocateLicense(
userId,
newLicenseId,
accountId,
);
} catch (e) {
this.logger.error(`error=${e}`);
if (e instanceof Error) {
@ -933,7 +940,10 @@ export class UsersService {
);
try {
await this.licensesRepository.deallocateLicense(userId);
const accountId = (await this.usersRepository.findUserById(userId))
.account_id;
await this.licensesRepository.deallocateLicense(userId, accountId);
} catch (e) {
this.logger.error(`error=${e}`);
if (e instanceof Error) {

View File

@ -0,0 +1,81 @@
import { ApiProperty } from '@nestjs/swagger';
import { Assignee } from '../../tasks/types/types';
import { ArrayMinSize, IsArray, IsInt, IsOptional, Min } from 'class-validator';
import { Type } from 'class-transformer';
import { Author } from '../../accounts/types/types';
export class WorkflowWorktype {
@ApiProperty({ description: 'Worktypeの内部ID' })
id: number;
@ApiProperty({ description: 'WorktypeID' })
worktypeId: string;
}
export class WorkflowTemplate {
@ApiProperty({ description: 'テンプレートの内部ID' })
id: number;
@ApiProperty({ description: 'テンプレートのファイル名' })
fileName: string;
}
export class Workflow {
@ApiProperty({ description: 'ワークフローの内部ID' })
id: number;
@ApiProperty({ description: 'Author情報' })
author: Author;
@ApiProperty({ description: 'Worktype情報', required: false })
worktype?: WorkflowWorktype;
@ApiProperty({ description: 'テンプレート情報', required: false })
template?: WorkflowTemplate;
@ApiProperty({
description: 'ルーティング候補のタイピストユーザー/タイピストグループ',
type: [Assignee],
})
typists: Assignee[];
}
export class GetWorkflowsResponse {
@ApiProperty({
description: 'ワークフローの一覧',
type: [Workflow],
})
workflows: Workflow[];
}
export class WorkflowTypist {
@ApiProperty({ description: 'タイピストユーザーの内部ID', required: false })
typistId?: number | undefined;
@ApiProperty({ description: 'タイピストグループの内部ID', required: false })
typistGroupId?: number | undefined;
}
export class CreateWorkflowsRequest {
@ApiProperty({ description: 'Authornの内部ID' })
@Type(() => Number)
@IsInt()
@Min(0)
authorId: number;
@ApiProperty({ description: 'Worktypeの内部ID', required: false })
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(0)
worktypeId?: number | undefined;
@ApiProperty({ description: 'テンプレートの内部ID', required: false })
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(0)
templateId?: number | undefined;
@ApiProperty({
description: 'ルーティング候補のタイピストユーザー/タイピストグループ',
type: [WorkflowTypist],
minItems: 1,
})
@Type(() => WorkflowTypist)
@IsArray()
@ArrayMinSize(1)
typists: WorkflowTypist[];
}
export class CreateWorkflowsResponse {}

View File

@ -0,0 +1,30 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ConfigModule } from '@nestjs/config';
import { WorkflowsService } from './workflows.service';
import { WorkflowsController } from './workflows.controller';
describe('WorkflowsController', () => {
let controller: WorkflowsController;
const mockTemplatesService = {};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
envFilePath: ['.env.local', '.env'],
isGlobal: true,
}),
],
controllers: [WorkflowsController],
providers: [WorkflowsService],
})
.overrideProvider(WorkflowsService)
.useValue(mockTemplatesService)
.compile();
controller = module.get<WorkflowsController>(WorkflowsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

@ -0,0 +1,112 @@
import {
Body,
Controller,
Get,
HttpStatus,
Post,
Req,
UseGuards,
} from '@nestjs/common';
import {
ApiBearerAuth,
ApiOperation,
ApiResponse,
ApiTags,
} from '@nestjs/swagger';
import jwt from 'jsonwebtoken';
import { AccessToken } from '../../common/token';
import { ErrorResponse } from '../../common/error/types/types';
import {
GetWorkflowsResponse,
CreateWorkflowsRequest,
CreateWorkflowsResponse,
} from './types/types';
import { AuthGuard } from '../../common/guards/auth/authguards';
import { RoleGuard } from '../../common/guards/role/roleguards';
import { ADMIN_ROLES } from '../../constants';
import { retrieveAuthorizationToken } from '../../common/http/helper';
import { Request } from 'express';
import { makeContext } from '../../common/log';
import { WorkflowsService } from './workflows.service';
@ApiTags('workflows')
@Controller('workflows')
export class WorkflowsController {
constructor(private readonly workflowsService: WorkflowsService) {}
@ApiResponse({
status: HttpStatus.OK,
type: GetWorkflowsResponse,
description: '成功時のレスポンス',
})
@ApiResponse({
status: HttpStatus.UNAUTHORIZED,
description: '認証エラー',
type: ErrorResponse,
})
@ApiResponse({
status: HttpStatus.INTERNAL_SERVER_ERROR,
description: '想定外のサーバーエラー',
type: ErrorResponse,
})
@ApiOperation({
operationId: 'getWorkflows',
description: 'アカウント内のワークフローの一覧を取得します',
})
@ApiBearerAuth()
@UseGuards(AuthGuard)
@UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] }))
@Get()
async getWorkflows(@Req() req: Request): Promise<GetWorkflowsResponse> {
const token = retrieveAuthorizationToken(req);
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
const context = makeContext(userId);
console.log(context.trackingId);
return { workflows: [] };
}
@ApiResponse({
status: HttpStatus.OK,
type: CreateWorkflowsResponse,
description: '成功時のレスポンス',
})
@ApiResponse({
status: HttpStatus.BAD_REQUEST,
description: 'パラメータ不正エラー',
type: ErrorResponse,
})
@ApiResponse({
status: HttpStatus.UNAUTHORIZED,
description: '認証エラー',
type: ErrorResponse,
})
@ApiResponse({
status: HttpStatus.INTERNAL_SERVER_ERROR,
description: '想定外のサーバーエラー',
type: ErrorResponse,
})
@ApiOperation({
operationId: 'createWorkflows',
description: 'アカウント内にワークフローを新規作成します',
})
@ApiBearerAuth()
@UseGuards(AuthGuard)
@UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] }))
@Post()
async createWorkflows(
@Req() req: Request,
@Body() body: CreateWorkflowsRequest,
): Promise<CreateWorkflowsResponse> {
const { authorId } = body;
const token = retrieveAuthorizationToken(req);
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
const context = makeContext(userId);
console.log(context.trackingId);
console.log(authorId);
return {};
}
}

View File

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { UsersRepositoryModule } from '../../repositories/users/users.repository.module';
import { WorkflowsController } from './workflows.controller';
import { WorkflowsService } from './workflows.service';
@Module({
imports: [UsersRepositoryModule],
providers: [WorkflowsService],
controllers: [WorkflowsController],
})
export class WorkflowsModule {}

View File

@ -0,0 +1,7 @@
import { Injectable, Logger } from '@nestjs/common';
@Injectable()
export class WorkflowsService {
private readonly logger = new Logger(WorkflowsService.name);
constructor() {}
}

View File

@ -25,6 +25,7 @@ export class BlobstorageService {
private readonly sharedKeyCredentialUS: StorageSharedKeyCredential;
private readonly sharedKeyCredentialAU: StorageSharedKeyCredential;
private readonly sharedKeyCredentialEU: StorageSharedKeyCredential;
private readonly sasTokenExpireHour: number;
constructor(private readonly configService: ConfigService) {
this.sharedKeyCredentialUS = new StorageSharedKeyCredential(
this.configService.get('STORAGE_ACCOUNT_NAME_US'),
@ -50,6 +51,14 @@ export class BlobstorageService {
this.configService.get('STORAGE_ACCOUNT_ENDPOINT_EU'),
this.sharedKeyCredentialEU,
);
const expireTime = Number(
this.configService.get('STORAGE_TOKEN_EXPIRE_TIME'),
);
if (Number.isNaN(expireTime)) {
throw new Error(`STORAGE_TOKEN_EXPIRE_TIME is invalid value NaN`);
}
this.sasTokenExpireHour = expireTime;
}
/**
@ -206,10 +215,7 @@ export class BlobstorageService {
//SASの有効期限を設定
const expiryDate = new Date();
expiryDate.setHours(
expiryDate.getHours() +
this.configService.get('STORAGE_TOKEN_EXPIRE_TIME'),
);
expiryDate.setHours(expiryDate.getHours() + this.sasTokenExpireHour);
//SASの権限を設定。Pendingにしたものを再アップロードする運用をするため、上書き可能にする
const permissions = new ContainerSASPermissions();
@ -237,6 +243,59 @@ export class BlobstorageService {
return url.toString();
}
/**
* SASトークン付きのBlobStorageテンプレートファイルアップロードURLを生成し返却します
* @param accountId
* @param country
* @returns template upload sas
*/
async publishTemplateUploadSas(
context: Context,
accountId: number,
country: string,
): Promise<string> {
this.logger.log(
`[IN] [${context.trackingId}] ${this.publishTemplateUploadSas.name}`,
);
try {
// コンテナ名を指定してClientを取得
const containerClient = this.getContainerClient(accountId, country);
// 国に対応したリージョンの接続情報を取得する
const sharedKeyCredential = this.getSharedKeyCredential(country);
//SASの有効期限を設定
const expiryDate = new Date();
expiryDate.setHours(expiryDate.getHours() + this.sasTokenExpireHour);
//SASの権限を設定。同名ファイルを再アップロードできる運用をするため、上書き可能にする
const permissions = new ContainerSASPermissions();
permissions.write = true;
//SASを発行
const sasToken = generateBlobSASQueryParameters(
{
containerName: containerClient.containerName,
permissions: permissions,
startsOn: new Date(),
expiresOn: expiryDate,
},
sharedKeyCredential,
);
// baseパスの末尾に/をつけないとパスの最後のセグメントが無視される
const url = new URL('Templates', `${containerClient.url}/`);
url.search = `${sasToken}`;
return url.toString();
} catch (e) {
this.logger.error(`error=${e}`);
throw e;
} finally {
this.logger.log(
`[OUT] [${context.trackingId}] ${this.publishTemplateUploadSas.name}`,
);
}
}
/**
* SASトークン付きのBlobStorageダウンロードURLを生成し返却します
* @param accountId
@ -274,10 +333,7 @@ export class BlobstorageService {
//SASの有効期限を設定
const expiryDate = new Date();
expiryDate.setHours(
expiryDate.getHours() +
this.configService.get('STORAGE_TOKEN_EXPIRE_TIME'),
);
expiryDate.setHours(expiryDate.getHours() + this.sasTokenExpireHour);
//SASの権限を設定ダウンロードのため読み取り許可
const permissions = new BlobSASPermissions();

View File

@ -5,11 +5,17 @@ import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
import helmet from 'helmet';
const helmetDirectives = helmet.contentSecurityPolicy.getDefaultDirectives();
helmetDirectives['connect-src'] = [
"'self'",
'https://adb2codmsdev.b2clogin.com/adb2codmsdev.onmicrosoft.com/b2c_1_signin_dev/v2.0/.well-known/openid-configuration',
'https://adb2codmsdev.b2clogin.com/adb2codmsdev.onmicrosoft.com/b2c_1_signin_dev/oauth2/v2.0/token',
];
helmetDirectives['connect-src'] =
process.env.STAGE === 'local'
? [
"'self'",
process.env.ADB2C_ORIGIN,
process.env.STORAGE_ACCOUNT_ENDPOINT_US,
process.env.STORAGE_ACCOUNT_ENDPOINT_AU,
process.env.STORAGE_ACCOUNT_ENDPOINT_EU,
]
: ["'self'"];
helmetDirectives['navigate-to'] = ["'self'"];
helmetDirectives['style-src'] = ["'self'", 'https:'];
helmetDirectives['report-uri'] = ["'self'"];

View File

@ -160,6 +160,9 @@ export class LicenseAllocationHistory {
@Column()
is_allocated: boolean;
@Column()
account_id: number;
@Column()
executed_at: Date;

View File

@ -452,7 +452,11 @@ export class LicensesRepositoryService {
* @param userId
* @param newLicenseId
*/
async allocateLicense(userId: number, newLicenseId: number): Promise<void> {
async allocateLicense(
userId: number,
newLicenseId: number,
accountId: number,
): Promise<void> {
await this.dataSource.transaction(async (entityManager) => {
const licenseRepo = entityManager.getRepository(License);
const licenseAllocationHistoryRepo = entityManager.getRepository(
@ -503,6 +507,7 @@ export class LicensesRepositoryService {
const deallocationHistory = new LicenseAllocationHistory();
deallocationHistory.user_id = userId;
deallocationHistory.license_id = allocatedLicense.id;
deallocationHistory.account_id = accountId;
deallocationHistory.is_allocated = false;
deallocationHistory.executed_at = new Date();
deallocationHistory.switch_from_type = SWITCH_FROM_TYPE.NONE;
@ -548,6 +553,7 @@ export class LicensesRepositoryService {
const allocationHistory = new LicenseAllocationHistory();
allocationHistory.user_id = userId;
allocationHistory.license_id = targetLicense.id;
allocationHistory.account_id = accountId;
allocationHistory.is_allocated = true;
allocationHistory.executed_at = new Date();
// TODO switchFromTypeの値については「PBI1234: 第一階層として、ライセンス数推移情報をCSV出力したい」で正式対応
@ -561,7 +567,7 @@ export class LicensesRepositoryService {
*
* @param userId
*/
async deallocateLicense(userId: number): Promise<void> {
async deallocateLicense(userId: number, accountId: number): Promise<void> {
await this.dataSource.transaction(async (entityManager) => {
const licenseRepo = entityManager.getRepository(License);
const licenseAllocationHistoryRepo = entityManager.getRepository(
@ -591,6 +597,7 @@ export class LicensesRepositoryService {
const deallocationHistory = new LicenseAllocationHistory();
deallocationHistory.user_id = userId;
deallocationHistory.license_id = allocatedLicense.id;
deallocationHistory.account_id = accountId;
deallocationHistory.is_allocated = false;
deallocationHistory.executed_at = new Date();
deallocationHistory.switch_from_type = SWITCH_FROM_TYPE.NONE;

View File

@ -18,12 +18,12 @@ export class TemplateFile {
url: string;
@Column()
file_name: string;
@Column()
created_by: string;
@Column({ nullable: true })
created_by?: string;
@CreateDateColumn()
created_at: Date;
@Column()
updated_by: string;
@Column({ nullable: true })
updated_by?: string;
@UpdateDateColumn()
updated_at: Date;
@OneToMany(() => Task, (task) => task.template_file)

View File

@ -22,4 +22,40 @@ export class TemplateFilesRepositoryService {
return templates;
});
}
/**
*
* @param accountId
* @param fileName
* @param url
* @returns template file
*/
async upsertTemplateFile(
accountId: number,
fileName: string,
url: string,
): Promise<void> {
await this.dataSource.transaction(async (entityManager) => {
const templateFilesRepo = entityManager.getRepository(TemplateFile);
// アカウント内に同名ファイルがあるか確認
const template = await templateFilesRepo.findOne({
where: { account_id: accountId, file_name: fileName },
});
// 同名ファイルは同じものとして扱うため、すでにファイルがあれば更新(更新日時の履歴を残しておきたい)
if (template) {
await templateFilesRepo.update(
{ id: template.id },
{ file_name: fileName, url: url },
);
} else {
const newTemplate = new TemplateFile();
newTemplate.account_id = accountId;
newTemplate.file_name = fileName;
newTemplate.url = url;
await templateFilesRepo.save(newTemplate);
}
});
}
}