Merged PR 184: ローディング表現に関する実装等を行う

## 概要
[Task2015: [Task1895完了後][Sp11-2着手] ローディング表現に関する実装等を行う](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2015)

- 現在実装中のテーブル、ポップアップについてローディング表現を実装しました。
  - ローディング中にローディング中を示すぐるぐるを表示
  - ローディング中はボタンを非活性にする

## レビューポイント
- 対応箇所は適切か
- 表示内容に問題はないか

## UIの変更
- [Task2015](https://ndstokyo.sharepoint.com/:f:/r/sites/Piranha/Shared%20Documents/General/OMDS/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88/Task2015?csf=1&web=1&e=TmrOXa)

## 動作確認状況
- ローカルで確認
This commit is contained in:
makabe.t 2023-06-30 05:51:14 +00:00
parent 539308f5b4
commit e4bc4776b0
27 changed files with 269 additions and 108 deletions

View File

@ -5,5 +5,3 @@ jest.config.js
vite.config.ts
.env.local
# デザイナのcssから生成するため除外
src/styles/app.module.scss.d.ts

View File

@ -6,6 +6,7 @@ import {
listTasksAsync,
listTypistGroupsAsync,
listTypistsAsync,
updateAssigneeAsync,
} from "./operations";
import {
SORTABLE_COLUMN,
@ -35,6 +36,7 @@ const initialState: DictationState = {
selected: [],
pool: [],
},
isLoading: true,
},
};
@ -96,11 +98,19 @@ export const dictationSlice = createSlice({
},
},
extraReducers: (builder) => {
builder.addCase(listTasksAsync.pending, (state) => {
state.apps.isLoading = true;
});
builder.addCase(listTasksAsync.fulfilled, (state, action) => {
state.domain.limit = action.payload.limit;
state.domain.offset = action.payload.offset;
state.domain.total = action.payload.total;
state.domain.tasks = action.payload.tasks;
state.apps.isLoading = false;
});
builder.addCase(listTasksAsync.rejected, (state) => {
state.apps.isLoading = false;
});
builder.addCase(getSortColumnAsync.fulfilled, (state, action) => {
state.apps.direction = action.payload.direction;
@ -112,6 +122,15 @@ export const dictationSlice = createSlice({
builder.addCase(listTypistGroupsAsync.fulfilled, (state, action) => {
state.domain.typistGroups = action.payload.typistGroups;
});
builder.addCase(updateAssigneeAsync.pending, (state) => {
state.apps.isLoading = true;
});
builder.addCase(updateAssigneeAsync.fulfilled, (state) => {
state.apps.isLoading = false;
});
builder.addCase(updateAssigneeAsync.rejected, (state) => {
state.apps.isLoading = false;
});
},
});

View File

@ -275,13 +275,6 @@ export const updateAssigneeAsync = createAsyncThunk<
headers: { authorization: `Bearer ${accessToken}` },
}
);
thunkApi.dispatch(
openSnackbar({
level: "info",
message: getTranslationID("common.message.success"),
})
);
return {};
} catch (e) {
// e ⇒ errorObjectに変換"

View File

@ -38,3 +38,6 @@ export const selectSelectedTranscriptionists = (state: RootState) =>
export const selectPoolTranscriptionists = (state: RootState) =>
state.dictation.apps.assignee.pool;
export const selectIsLoading = (state: RootState) =>
state.dictation.apps.isLoading;

View File

@ -28,4 +28,5 @@ export interface Apps {
selected: Assignee[];
pool: Assignee[];
};
isLoading: boolean;
}

View File

@ -1,10 +1,12 @@
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
import { LicenseOrdersState } from "./state";
import { orderLicenseAsync } from "./operations";
const initialState: LicenseOrdersState = {
apps: {
poNumber: "",
newOrder: 0,
isLoading: false,
},
};
export const licenseSlice = createSlice({
@ -23,6 +25,17 @@ export const licenseSlice = createSlice({
state.apps = initialState.apps;
},
},
extraReducers: (builder) => {
builder.addCase(orderLicenseAsync.pending, (state) => {
state.apps.isLoading = true;
});
builder.addCase(orderLicenseAsync.fulfilled, (state) => {
state.apps.isLoading = false;
});
builder.addCase(orderLicenseAsync.rejected, (state) => {
state.apps.isLoading = false;
});
},
});
export const { changePoNumber, changeNewOrder, cleanupApps } =

View File

@ -44,7 +44,9 @@ export const orderLicenseAsync = createAsyncThunk<
thunkApi.dispatch(
openSnackbar({
level: "info",
message: getTranslationID("common.message.success"),
message: getTranslationID(
"licenseOrderPage.message.createOrderSuccess"
),
})
);
return {};

View File

@ -34,3 +34,6 @@ export const checkErrorIncorrectNewOrder = (newOrder: number): boolean => {
export const selectPoNumber = (state: RootState) => state.license.apps.poNumber;
export const selectNewOrder = (state: RootState) => state.license.apps.newOrder;
export const selectIsLoading = (state: RootState) =>
state.license.apps.isLoading;

View File

@ -5,4 +5,5 @@ export interface LicenseOrdersState {
export interface Apps {
poNumber: string;
newOrder: number;
isLoading: boolean;
}

View File

@ -16,6 +16,9 @@ const initialState: LicenseSummaryState = {
usedSize: 0,
isAccountLock: false,
},
apps: {
isLoading: false,
},
};
export const licenseSummarySlice = createSlice({

View File

@ -3,3 +3,5 @@ import { RootState } from "app/store";
// 各値はそのまま画面に表示するので、licenseSummaryInfoとして値を取得する
export const selecLicenseSummaryInfo = (state: RootState) =>
state.licenseSummary.domain;
export const selectIsLoading = (state: RootState) => state.license;

View File

@ -1,5 +1,6 @@
export interface LicenseSummaryState {
domain: Domain;
apps: Apps;
}
export interface Domain {
@ -15,3 +16,7 @@ export interface Domain {
usedSize: number;
isAccountLock: boolean;
}
export interface Apps {
isLoading: boolean;
}

View File

@ -53,3 +53,4 @@ export const selectLicenseAlert = (state: RootState) =>
export const selectNtotification = (state: RootState) =>
state.user.apps.addUser.notification;
export const selectDomain = (state: RootState) => state.user.domain;
export const selectIsLoading = (state: RootState) => state.user.apps.isLoading;

View File

@ -11,6 +11,7 @@ export interface Domain {
export interface Apps {
addUser: AddUser;
isLoading: boolean;
}
export interface AddUser extends User {

View File

@ -1,6 +1,6 @@
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
import { UsersState } from "./state";
import { listUsersAsync } from "./operations";
import { addUserAsync, listUsersAsync } from "./operations";
import { ROLE, RoleType } from "./constants";
const initialState: UsersState = {
@ -17,6 +17,7 @@ const initialState: UsersState = {
licenseAlert: true,
notification: true,
},
isLoading: false,
},
};
@ -73,8 +74,24 @@ export const userSlice = createSlice({
},
},
extraReducers: (builder) => {
builder.addCase(listUsersAsync.pending, (state) => {
state.apps.isLoading = true;
});
builder.addCase(listUsersAsync.fulfilled, (state, action) => {
state.domain.users = action.payload.users;
state.apps.isLoading = false;
});
builder.addCase(listUsersAsync.rejected, (state) => {
state.apps.isLoading = false;
});
builder.addCase(addUserAsync.pending, (state) => {
state.apps.isLoading = true;
});
builder.addCase(addUserAsync.fulfilled, (state) => {
state.apps.isLoading = false;
});
builder.addCase(addUserAsync.rejected, (state) => {
state.apps.isLoading = false;
});
},
});

View File

@ -3,6 +3,7 @@ import styles from "styles/app.module.scss";
import { useDispatch, useSelector } from "react-redux";
import {
changeAssignee,
selectIsLoading,
selectPoolTranscriptionists,
selectSelectedTask,
selectSelectedTranscriptionists,
@ -13,6 +14,7 @@ import { AppDispatch } from "app/store";
import { getTranslationID } from "translation";
import { useTranslation } from "react-i18next";
import close from "../../assets/images/close.svg";
import progress_activit from "../../assets/images/progress_activit.svg";
interface ChangeTranscriptionistPopupProps {
onClose: (isChanged: boolean) => void;
@ -26,6 +28,8 @@ export const ChangeTranscriptionistPopup: React.FC<
const dispatch: AppDispatch = useDispatch();
const [t] = useTranslation();
const isLoading = useSelector(selectIsLoading);
// ポップアップを閉じる処理
const closePopup = useCallback(() => {
onClose(false);
@ -182,9 +186,17 @@ export const ChangeTranscriptionistPopup: React.FC<
type="button"
name="submit"
value={t(getTranslationID("dictationPage.label.saveChanges"))}
className={`${styles.formSubmit} ${styles.marginBtm1} ${styles.isActive}`}
className={`${styles.formSubmit} ${styles.marginBtm1} ${
!isLoading ? styles.isActive : ""
}`}
onClick={onChangeTranscriptionist}
/>
<img
style={{ display: isLoading ? "inline" : "none" }}
src={progress_activit}
className={styles.icLoading}
alt="Loading"
/>
</dd>
</dl>
</form>

View File

@ -28,6 +28,7 @@ import {
changeAssignee,
listTypistsAsync,
listTypistGroupsAsync,
selectIsLoading,
} from "features/dictation";
import { getTranslationID } from "translation";
import { Task } from "api/api";
@ -39,6 +40,7 @@ import inprogress from "../../assets/images/inprogress.svg";
import finished from "../../assets/images/finished.svg";
import backup from "../../assets/images/backup.svg";
import lock from "../../assets/images/lock.svg";
import progress_activit from "../../assets/images/progress_activit.svg";
import { DisPlayInfo } from "./displayInfo";
import { ChangeTranscriptionistPopup } from "./changeTranscriptionistPopup";
@ -82,6 +84,8 @@ const DictationPage: React.FC = (): JSX.Element => {
const totalPage = useSelector(selectTotalPage);
const currentPage = useSelector(selectCurrentPage);
const isLoading = useSelector(selectIsLoading);
// ページネーションのボタンクリック時のアクション
const getFirstPage = useCallback(() => {
const filter = getFilter(
@ -421,6 +425,7 @@ const DictationPage: React.FC = (): JSX.Element => {
value="flUploaded"
className={styles.formCheck}
checked={filterUploaded}
disabled={isLoading}
onChange={(e) => {
setFilterUploaded(e.target.checked);
updateFilter(
@ -443,6 +448,7 @@ const DictationPage: React.FC = (): JSX.Element => {
value="flInProgress"
className={styles.formCheck}
checked={filterInProgress}
disabled={isLoading}
onChange={(e) => {
setFilterInProgress(e.target.checked);
updateFilter(
@ -465,6 +471,7 @@ const DictationPage: React.FC = (): JSX.Element => {
value="flPending"
className={styles.formCheck}
checked={filterPending}
disabled={isLoading}
onChange={(e) => {
setFilterPending(e.target.checked);
updateFilter(
@ -487,6 +494,7 @@ const DictationPage: React.FC = (): JSX.Element => {
value="flFinished"
className={styles.formCheck}
checked={filterFinished}
disabled={isLoading}
onChange={(e) => {
setFilterFinished(e.target.checked);
updateFilter(
@ -509,6 +517,7 @@ const DictationPage: React.FC = (): JSX.Element => {
value="flBackup"
className={styles.formCheck}
checked={filterBackup}
disabled={isLoading}
onChange={(e) => {
setFilterBackup(e.target.checked);
updateFilter(
@ -537,6 +546,9 @@ const DictationPage: React.FC = (): JSX.Element => {
onClick={() =>
updateSortColumn(SORTABLE_COLUMN.JobNumber)
}
style={{
pointerEvents: isLoading ? "none" : "auto",
}}
>
{t(
getTranslationID("dictationPage.label.jobNumber")
@ -552,6 +564,9 @@ const DictationPage: React.FC = (): JSX.Element => {
onClick={() =>
updateSortColumn(SORTABLE_COLUMN.Status)
}
style={{
pointerEvents: isLoading ? "none" : "auto",
}}
>
{t(getTranslationID("dictationPage.label.status"))}
</a>
@ -568,6 +583,9 @@ const DictationPage: React.FC = (): JSX.Element => {
onClick={() =>
updateSortColumn(SORTABLE_COLUMN.Encryption)
}
style={{
pointerEvents: isLoading ? "none" : "auto",
}}
>
{t(
getTranslationID("dictationPage.label.encryption")
@ -583,6 +601,9 @@ const DictationPage: React.FC = (): JSX.Element => {
onClick={() =>
updateSortColumn(SORTABLE_COLUMN.AuthorId)
}
style={{
pointerEvents: isLoading ? "none" : "auto",
}}
>
{t(
getTranslationID("dictationPage.label.authorId")
@ -598,6 +619,9 @@ const DictationPage: React.FC = (): JSX.Element => {
onClick={() =>
updateSortColumn(SORTABLE_COLUMN.WorkType)
}
style={{
pointerEvents: isLoading ? "none" : "auto",
}}
>
{t(
getTranslationID("dictationPage.label.workType")
@ -613,6 +637,9 @@ const DictationPage: React.FC = (): JSX.Element => {
onClick={() =>
updateSortColumn(SORTABLE_COLUMN.FileName)
}
style={{
pointerEvents: isLoading ? "none" : "auto",
}}
>
{t(
getTranslationID("dictationPage.label.fileName")
@ -628,6 +655,9 @@ const DictationPage: React.FC = (): JSX.Element => {
onClick={() =>
updateSortColumn(SORTABLE_COLUMN.FileLength)
}
style={{
pointerEvents: isLoading ? "none" : "auto",
}}
>
{t(
getTranslationID("dictationPage.label.fileLength")
@ -643,6 +673,9 @@ const DictationPage: React.FC = (): JSX.Element => {
onClick={() =>
updateSortColumn(SORTABLE_COLUMN.FileSize)
}
style={{
pointerEvents: isLoading ? "none" : "auto",
}}
>
{t(
getTranslationID("dictationPage.label.fileSize")
@ -660,6 +693,9 @@ const DictationPage: React.FC = (): JSX.Element => {
SORTABLE_COLUMN.RecordingStartedDate
)
}
style={{
pointerEvents: isLoading ? "none" : "auto",
}}
>
{t(
getTranslationID(
@ -679,6 +715,9 @@ const DictationPage: React.FC = (): JSX.Element => {
SORTABLE_COLUMN.RecordingFinishedDate
)
}
style={{
pointerEvents: isLoading ? "none" : "auto",
}}
>
{t(
getTranslationID(
@ -696,6 +735,9 @@ const DictationPage: React.FC = (): JSX.Element => {
onClick={() =>
updateSortColumn(SORTABLE_COLUMN.UploadDate)
}
style={{
pointerEvents: isLoading ? "none" : "auto",
}}
>
{t(
getTranslationID("dictationPage.label.uploadDate")
@ -713,6 +755,9 @@ const DictationPage: React.FC = (): JSX.Element => {
SORTABLE_COLUMN.TranscriptionStartedDate
)
}
style={{
pointerEvents: isLoading ? "none" : "auto",
}}
>
{t(
getTranslationID(
@ -732,6 +777,9 @@ const DictationPage: React.FC = (): JSX.Element => {
SORTABLE_COLUMN.TranscriptionFinishedDate
)
}
style={{
pointerEvents: isLoading ? "none" : "auto",
}}
>
{t(
getTranslationID(
@ -826,7 +874,8 @@ const DictationPage: React.FC = (): JSX.Element => {
</th>
)}
</tr>
{tasks.length !== 0 &&
{(isChangeTranscriptionistPopupOpen || !isLoading) &&
tasks.length !== 0 &&
tasks.map((x) => (
<tr key={x.audioFileId}>
<td className={styles.clm0}>
@ -1032,13 +1081,21 @@ const DictationPage: React.FC = (): JSX.Element => {
</tr>
))}
</table>
{tasks.length === 0 && (
<p style={{ margin: "10px", textAlign: "center" }}>
{t(getTranslationID("common.message.listEmpty"))}
</p>
)}
{(isChangeTranscriptionistPopupOpen || !isLoading) &&
tasks.length === 0 && (
<p style={{ margin: "10px", textAlign: "center" }}>
{t(getTranslationID("common.message.listEmpty"))}
</p>
)}
</div>
{isLoading && (
<img
style={{ position: "sticky" }}
src={progress_activit}
className={styles.icLoading}
alt="Loading"
/>
)}
{/** pagenation */}
<div className={styles.pagenation}>
<nav className={styles.pagenationNav}>
@ -1047,14 +1104,18 @@ const DictationPage: React.FC = (): JSX.Element => {
)}`}</span>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
<a
className={`${currentPage !== 1 ? styles.isActive : ""}`}
className={`${
!isLoading && currentPage !== 1 ? styles.isActive : ""
}`}
onClick={getFirstPage}
>
«
</a>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
<a
className={`${currentPage !== 1 ? styles.isActive : ""}`}
className={`${
!isLoading && currentPage !== 1 ? styles.isActive : ""
}`}
onClick={getPrevPage}
>
@ -1063,7 +1124,9 @@ const DictationPage: React.FC = (): JSX.Element => {
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
<a
className={`${
currentPage < totalPage ? styles.isActive : ""
!isLoading && currentPage < totalPage
? styles.isActive
: ""
}`}
onClick={getNextPage}
>
@ -1072,7 +1135,9 @@ const DictationPage: React.FC = (): JSX.Element => {
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
<a
className={`${
currentPage < totalPage ? styles.isActive : ""
!isLoading && currentPage < totalPage
? styles.isActive
: ""
}`}
onClick={getLastPage}
>

View File

@ -12,8 +12,10 @@ import {
selectInputValidationErrors,
orderLicenseAsync,
cleanupApps,
selectIsLoading,
} from "features/license/licenseOrder";
import close from "../../assets/images/close.svg";
import progress_activit from "../../assets/images/progress_activit.svg";
interface LicenseOrderPopupProps {
onClose: () => void;
@ -27,6 +29,8 @@ export const LicenseOrderPopup: React.FC<LicenseOrderPopupProps> = (props) => {
const [count, setCount] = useState<number>(initCount);
const [poNumberInputValue, setpoNumberInputValue] = useState<string>();
const isLoading = useSelector(selectIsLoading);
useEffect(
() => () => {
// useEffectのreturnとしてcleanupAppsを実行することで、ポップアップのアンマウント時に初期化を行う
@ -199,9 +203,17 @@ export const LicenseOrderPopup: React.FC<LicenseOrderPopupProps> = (props) => {
value={t(
getTranslationID("licenseOrderPage.label.orderButton")
)}
className={`${styles.formSubmit} ${styles.marginBtm1} ${styles.isActive}`}
className={`${styles.formSubmit} ${styles.marginBtm1} ${
!isLoading ? styles.isActive : ""
}`}
onClick={onOrderLicense}
/>
<img
style={{ display: isLoading ? "inline" : "none" }}
src={progress_activit}
className={styles.icLoading}
alt="Loading"
/>
</dd>
</dl>
</form>

View File

@ -5,7 +5,7 @@ import Footer from "components/footer";
import styles from "styles/app.module.scss";
import { UpdateTokenTimer } from "components/auth/updateTokenTimer";
import { useDispatch, useSelector } from "react-redux";
import { listUsersAsync, selectDomain } from "features/user";
import { listUsersAsync, selectDomain, selectIsLoading } from "features/user";
import { useTranslation } from "react-i18next";
import { getTranslationID } from "translation";
import personAdd from "../../assets/images/person_add.svg";
@ -14,6 +14,7 @@ import deleteImg from "../../assets/images/delete.svg";
import badgeImg from "../../assets/images/badge.svg";
import checkFill from "../../assets/images/check_fill.svg";
import circle from "../../assets/images/circle.svg";
import progress_activit from "../../assets/images/progress_activit.svg";
import { UserAddPopup } from "./popup";
const UserListPage: React.FC = (): JSX.Element => {
@ -32,6 +33,7 @@ const UserListPage: React.FC = (): JSX.Element => {
}, [dispatch]);
const domain = useSelector(selectDomain);
const isLoading = useSelector(selectIsLoading);
return (
<>
@ -59,7 +61,9 @@ const UserListPage: React.FC = (): JSX.Element => {
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
<a
className={`${styles.menuLink} ${styles.isActive}`}
className={`${styles.menuLink} ${
!isLoading ? styles.isActive : ""
}`}
onClick={onOpen}
>
<img src={personAdd} alt="" className={styles.menuIcon} />
@ -143,65 +147,73 @@ const UserListPage: React.FC = (): JSX.Element => {
</th>
</tr>
{/* XXX 「固定」の項目と、isSelected、isAlertの対応が必要 */}
{domain.users.map((user) => (
<tr key={user.email}>
<td>{user.name}</td>
<td>{user.role}</td>
<td>{user.authorId}</td>
<td>{user.typistGroupName}</td>
<td>{user.email}</td>
<td>固定:Uploaded</td>
<td>固定:2023/8/3</td>
<td>固定:114</td>
<td>
{user.autoRenew ? (
<img
src={checkFill}
alt=""
className={styles.icCheckCircle}
/>
) : (
<img
src={circle}
alt=""
className={styles.icCheckCircle}
/>
)}
</td>
<td>
{user.licenseAlert ? (
<img
src={checkFill}
alt=""
className={styles.icCheckCircle}
/>
) : (
<img
src={circle}
alt=""
className={styles.icCheckCircle}
/>
)}
</td>
<td>
{user.notification ? (
<img
src={checkFill}
alt=""
className={styles.icCheckCircle}
/>
) : (
<img
src={circle}
alt=""
className={styles.icCheckCircle}
/>
)}
</td>
</tr>
))}
{(isPopupOpen || !isLoading) &&
domain.users.map((user) => (
<tr key={user.email}>
<td>{user.name}</td>
<td>{user.role}</td>
<td>{user.authorId}</td>
<td>{user.typistGroupName}</td>
<td>{user.email}</td>
<td>固定:Uploaded</td>
<td>固定:2023/8/3</td>
<td>固定:114</td>
<td>
{user.autoRenew ? (
<img
src={checkFill}
alt=""
className={styles.icCheckCircle}
/>
) : (
<img
src={circle}
alt=""
className={styles.icCheckCircle}
/>
)}
</td>
<td>
{user.licenseAlert ? (
<img
src={checkFill}
alt=""
className={styles.icCheckCircle}
/>
) : (
<img
src={circle}
alt=""
className={styles.icCheckCircle}
/>
)}
</td>
<td>
{user.notification ? (
<img
src={checkFill}
alt=""
className={styles.icCheckCircle}
/>
) : (
<img
src={circle}
alt=""
className={styles.icCheckCircle}
/>
)}
</td>
</tr>
))}
</tbody>
</table>
{isLoading && (
<img
src={progress_activit}
className={styles.icLoading}
alt="Loading"
/>
)}
<div className={styles.pagenation}>
<nav className={styles.pagenationNav}>
<span className={styles.pagenationTotal}>

View File

@ -25,8 +25,10 @@ import {
selectNtotification,
addUserAsync,
ROLE,
selectIsLoading,
} from "features/user";
import close from "../../assets/images/close.svg";
import progress_activit from "../../assets/images/progress_activit.svg";
interface UserAddPopupProps {
isOpen: boolean;
@ -63,6 +65,8 @@ export const UserAddPopup: React.FC<UserAddPopupProps> = (props) => {
const [isPushCreateButton, setIsPushCreateButton] = useState<boolean>(false);
const isLoading = useSelector(selectIsLoading);
const onAddUser = useCallback(async () => {
setIsPushCreateButton(true);
if (
@ -329,9 +333,17 @@ export const UserAddPopup: React.FC<UserAddPopupProps> = (props) => {
type="button"
name="submit"
value="Add user"
className={`${styles.formSubmit} ${styles.marginBtm1} ${styles.isActive}`}
className={`${styles.formSubmit} ${styles.marginBtm1} ${
!isLoading ? styles.isActive : ""
}`}
onClick={onAddUser}
/>
<img
style={{ display: isLoading ? "inline" : "none" }}
src={progress_activit}
className={styles.icLoading}
alt="Loading"
/>
</dd>
</dl>
</form>

View File

@ -87,7 +87,7 @@ declare const classNames: {
readonly isDisable: "isDisable";
readonly icCheckCircle: "icCheckCircle";
readonly icInTable: "icInTable";
readonly 'svg"': 'svg"';
readonly svg: "svg";
readonly history: "history";
readonly cardHistory: "cardHistory";
readonly partner: "partner";

View File

@ -6,8 +6,7 @@
"emailIncorrectError": "(de)Error Message",
"internalServerError": "(de)処理に失敗しました。時間をおいて再実行しても解決しない場合はシステム管理者にお問い合わせください。",
"listEmpty": "(de)検索結果が0件です。",
"dialogConfirm": "(de)操作を実行しますか?",
"success": "(de)処理に成功しました。"
"dialogConfirm": "(de)操作を実行しますか?"
},
"label": {
"cancel": "(de)Cancel",
@ -161,6 +160,7 @@
"poNumberIncorrectError": "(de)PO Numberの形式が不正です。PO Numberは半角英数字(大文字)のみ入力可能です。",
"newOrderIncorrectError": "(de)New Orderには1以上の数字を入力してください。",
"confirmOrder": "(de)注文を行いますか?",
"createOrderSuccess": "(de)処理に成功しました。",
"poNumberConflictError": "(de)既に同じPO Numberで注文済みもしくは発行済みの注文が存在しています。他のPO Numberで注文してください。"
},
"label": {

View File

@ -6,8 +6,7 @@
"emailIncorrectError": "Error Message",
"internalServerError": "処理に失敗しました。時間をおいて再実行しても解決しない場合はシステム管理者にお問い合わせください。",
"listEmpty": "検索結果が0件です。",
"dialogConfirm": "操作を実行しますか?",
"success": "処理に成功しました。"
"dialogConfirm": "操作を実行しますか?"
},
"label": {
"cancel": "Cancel",
@ -161,6 +160,7 @@
"poNumberIncorrectError": "PO Numberの形式が不正です。PO Numberは半角英数字(大文字)のみ入力可能です。",
"newOrderIncorrectError": "New Orderには1以上の数字を入力してください。",
"confirmOrder": "注文を行いますか?",
"createOrderSuccess": "処理に成功しました。",
"poNumberConflictError": "既に同じPO Numberで注文済みもしくは発行済みの注文が存在しています。他のPO Numberで注文してください。"
},
"label": {

View File

@ -6,8 +6,7 @@
"emailIncorrectError": "(es)Error Message",
"internalServerError": "(es)処理に失敗しました。時間をおいて再実行しても解決しない場合はシステム管理者にお問い合わせください。",
"listEmpty": "(es)検索結果が0件です。",
"dialogConfirm": "(es)操作を実行しますか?",
"success": "(es)処理に成功しました。"
"dialogConfirm": "(es)操作を実行しますか?"
},
"label": {
"cancel": "(es)Cancel",
@ -161,6 +160,7 @@
"poNumberIncorrectError": "(es)PO Numberの形式が不正です。PO Numberは半角英数字(大文字)のみ入力可能です。",
"newOrderIncorrectError": "(es)New Orderには1以上の数字を入力してください。",
"confirmOrder": "(es)注文を行いますか?",
"createOrderSuccess": "(es)処理に成功しました。",
"poNumberConflictError": "(es)既に同じPO Numberで注文済みもしくは発行済みの注文が存在しています。他のPO Numberで注文してください。"
},
"label": {

View File

@ -6,8 +6,7 @@
"emailIncorrectError": "(fr)Error Message",
"internalServerError": "(fr)処理に失敗しました。時間をおいて再実行しても解決しない場合はシステム管理者にお問い合わせください。",
"listEmpty": "(fr)検索結果が0件です。",
"dialogConfirm": "(fr)操作を実行しますか?",
"success": "(fr)処理に成功しました。"
"dialogConfirm": "(fr)操作を実行しますか?"
},
"label": {
"cancel": "(fr)Cancel",
@ -161,6 +160,7 @@
"poNumberIncorrectError": "(fr)PO Numberの形式が不正です。PO Numberは半角英数字(大文字)のみ入力可能です。",
"newOrderIncorrectError": "(fr)New Orderには1以上の数字を入力してください。",
"confirmOrder": "(fr)注文を行いますか?",
"createOrderSuccess": "(fr)処理に成功しました。",
"poNumberConflictError": "(fr)既に同じPO Numberで注文済みもしくは発行済みの注文が存在しています。他のPO Numberで注文してください。"
},
"label": {

View File

@ -1,15 +0,0 @@
-- +migrate Up
CREATE TABLE IF NOT EXISTS `template_files` (
`id` BIGINT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY COMMENT 'ID',
`account_id` BIGINT UNSIGNED NOT NULL COMMENT '紐づくアカウントID',
`url` VARCHAR(2048) NOT NULL COMMENT 'テンプレートファイルのURL',
`file_name` VARCHAR(1024) NOT NULL COMMENT 'テンプレートファイルのファイル名(拡張子付き)',
`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 '更新時刻'
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci;
-- +migrate Down
DROP TABLE `template_files`;

View File

@ -23,6 +23,7 @@
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"og": "openapi-generator-cli",
"openapi-format": "cat \"src/api/odms/openapi.json\" | jq -c . > \"src/api/odms/openapi.json\" && prettier --write \"src/api/odms/*.json\"",
"migrate:up": "sql-migrate up -config=/app/dictation_server/db/dbconfig.yml -env=local",
"migrate:down": "sql-migrate down -config=/app/dictation_server/db/dbconfig.yml -env=local"
},