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:
parent
539308f5b4
commit
e4bc4776b0
@ -5,5 +5,3 @@ jest.config.js
|
||||
vite.config.ts
|
||||
.env.local
|
||||
|
||||
# デザイナのcssから生成するため除外
|
||||
src/styles/app.module.scss.d.ts
|
||||
|
||||
@ -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;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -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に変換"
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -28,4 +28,5 @@ export interface Apps {
|
||||
selected: Assignee[];
|
||||
pool: Assignee[];
|
||||
};
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
@ -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 } =
|
||||
|
||||
@ -44,7 +44,9 @@ export const orderLicenseAsync = createAsyncThunk<
|
||||
thunkApi.dispatch(
|
||||
openSnackbar({
|
||||
level: "info",
|
||||
message: getTranslationID("common.message.success"),
|
||||
message: getTranslationID(
|
||||
"licenseOrderPage.message.createOrderSuccess"
|
||||
),
|
||||
})
|
||||
);
|
||||
return {};
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -5,4 +5,5 @@ export interface LicenseOrdersState {
|
||||
export interface Apps {
|
||||
poNumber: string;
|
||||
newOrder: number;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
@ -16,6 +16,9 @@ const initialState: LicenseSummaryState = {
|
||||
usedSize: 0,
|
||||
isAccountLock: false,
|
||||
},
|
||||
apps: {
|
||||
isLoading: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const licenseSummarySlice = createSlice({
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -11,6 +11,7 @@ export interface Domain {
|
||||
|
||||
export interface Apps {
|
||||
addUser: AddUser;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
export interface AddUser extends User {
|
||||
|
||||
@ -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;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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}
|
||||
>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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}>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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": {
|
||||
|
||||
@ -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": {
|
||||
|
||||
@ -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": {
|
||||
|
||||
@ -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": {
|
||||
|
||||
@ -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`;
|
||||
@ -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"
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user