diff --git a/web/e2e/project/content/view.spec.ts b/web/e2e/project/content/view.spec.ts index 4bf26ca3a6..b03491d320 100644 --- a/web/e2e/project/content/view.spec.ts +++ b/web/e2e/project/content/view.spec.ts @@ -56,6 +56,7 @@ test("View CRUD has succeeded", async ({ page }) => { await page.getByText("Remove View").click(); await page.getByRole("button", { name: "Remove" }).click(); await closeNotification(page, false); + await page.getByRole("button", { name: "Cancel" }).click(); await page.getByText("text", { exact: true }).click(); await expect( diff --git a/web/e2e/project/item/fields/option.spec.ts b/web/e2e/project/item/fields/option.spec.ts index 85dce4d234..604fa019fd 100644 --- a/web/e2e/project/item/fields/option.spec.ts +++ b/web/e2e/project/item/fields/option.spec.ts @@ -156,7 +156,7 @@ test("Option field editing has succeeded", async ({ page }) => { await page.getByRole("button", { name: "plus New" }).click(); await page.locator(".ant-select-selection-item").nth(1).click(); await page.getByTitle("new third").locator("div").last().click(); - await page.getByRole("button", { name: "delete" }).nth(1).click(); + await page.getByLabel("Update Option").getByRole("button", { name: "delete" }).last().click(); await page.getByRole("button", { name: "plus New" }).click(); await page.locator(".ant-select-selection-item").nth(1).click(); await page.getByTitle("new third").locator("div").last().click(); diff --git a/web/e2e/project/item/metadata/tag.spec.ts b/web/e2e/project/item/metadata/tag.spec.ts index c5df0352f7..0c5194dfd6 100644 --- a/web/e2e/project/item/metadata/tag.spec.ts +++ b/web/e2e/project/item/metadata/tag.spec.ts @@ -152,7 +152,7 @@ test("Tag metadata editing has succeeded", async ({ page }) => { await page.getByText("Tag3").nth(2).click(); await expect(page.getByLabel("Update Tag").getByText("Tag1Tag2Tag3")).toBeVisible(); await page.getByRole("tab", { name: "Settings" }).click(); - await page.getByRole("button", { name: "delete" }).first().click(); + await page.getByLabel("Update Tag").getByRole("button", { name: "delete" }).first().click(); await page.getByRole("tab", { name: "Default value" }).click(); await expect(page.getByLabel("Update Tag").getByText("Tag2Tag3")).toBeVisible(); await page.locator(".ant-select-selector").click(); @@ -177,6 +177,7 @@ test("Tag metadata editing has succeeded", async ({ page }) => { await closeNotification(page); await page.getByRole("cell").getByLabel("edit").locator("svg").first().click(); await expect(page.getByText("Tag3")).toBeVisible(); + await page.getByLabel("close-circle").locator("svg").hover(); await page.getByLabel("close-circle").locator("svg").click(); await expect(page.getByText("Please input field!")).toBeVisible(); await page.locator(".ant-select-selector").click(); diff --git a/web/src/components/molecules/AccountSettings/DangerZone.tsx b/web/src/components/molecules/AccountSettings/DangerZone.tsx index e8fcdf6fb2..233a3f0868 100644 --- a/web/src/components/molecules/AccountSettings/DangerZone.tsx +++ b/web/src/components/molecules/AccountSettings/DangerZone.tsx @@ -7,7 +7,7 @@ import ContentSection from "@reearth-cms/components/atoms/InnerContents/ContentS import Modal from "@reearth-cms/components/atoms/Modal"; import { useT } from "@reearth-cms/i18n"; -export interface Props { +interface Props { onUserDelete: () => Promise; } @@ -19,8 +19,9 @@ const DangerZone: React.FC = ({ onUserDelete }) => { confirm({ title: t("Are you sure you want to delete your account?"), icon: , - onOk() { - onUserDelete(); + cancelText: t("Cancel"), + async onOk() { + await onUserDelete(); }, }); }, [confirm, onUserDelete, t]); diff --git a/web/src/components/molecules/Asset/AssetList/index.tsx b/web/src/components/molecules/Asset/AssetList/index.tsx index c1f8a5567c..23e9552aae 100644 --- a/web/src/components/molecules/Asset/AssetList/index.tsx +++ b/web/src/components/molecules/Asset/AssetList/index.tsx @@ -25,6 +25,7 @@ interface Props { uploading: boolean; uploadModalVisibility: boolean; loading: boolean; + deleteLoading: boolean; uploadUrl: { url: string; autoUnzip: boolean }; uploadType: UploadType; selectedAsset?: Asset; @@ -61,6 +62,7 @@ const AssetList: React.FC = ({ uploading, uploadModalVisibility, loading, + deleteLoading, uploadUrl, uploadType, selectedAsset, @@ -144,6 +146,7 @@ const AssetList: React.FC = ({ assetList={assetList} selection={selection} loading={loading} + deleteLoading={deleteLoading} selectedAsset={selectedAsset} totalCount={totalCount} page={page} diff --git a/web/src/components/molecules/Asset/AssetListTable/index.tsx b/web/src/components/molecules/Asset/AssetListTable/index.tsx index 674792b3bc..5da70b5c98 100644 --- a/web/src/components/molecules/Asset/AssetListTable/index.tsx +++ b/web/src/components/molecules/Asset/AssetListTable/index.tsx @@ -31,7 +31,11 @@ import { compressedFileFormats } from "../../Common/Asset"; interface Props { assetList: Asset[]; + selection: { + selectedRowKeys: Key[]; + }; loading: boolean; + deleteLoading: boolean; selectedAsset?: Asset; totalCount: number; page: number; @@ -41,9 +45,6 @@ interface Props { onAssetSelect: (assetId: string) => void; onEdit: (assetId: string) => void; onSearchTerm: (term?: string) => void; - selection: { - selectedRowKeys: Key[]; - }; setSelection: (input: { selectedRowKeys: Key[] }) => void; onAssetsReload: () => void; onAssetDelete: (assetIds: string[]) => Promise; @@ -58,6 +59,7 @@ const AssetListTable: React.FC = ({ assetList, selection, loading, + deleteLoading, selectedAsset, totalCount, page, @@ -263,22 +265,32 @@ const AssetListTable: React.FC = ({ [onSearchTerm, searchTerm, t], ); - const AlertOptions = useCallback( + const alertOptions = useCallback( // eslint-disable-next-line @typescript-eslint/no-explicit-any (props: any) => { return ( - - - {t("Deselect")} - + + - onAssetDelete?.(props.selectedRowKeys)}> - {t("Delete")} - + ); }, - [onAssetDelete, t], + [deleteLoading, onAssetDelete, t], ); const handleChange = useCallback( @@ -308,7 +320,7 @@ const AssetListTable: React.FC = ({ = ({ centered open={visible} onCancel={onCancel} - footer={null} + footer={ + <> + + + + } width="50vw" afterClose={onUploadModalClose} styles={{ body: { minHeight: "50vh", - position: "relative", - paddingBottom: "80px", }, }}>
@@ -77,18 +88,6 @@ const UploadModal: React.FC = ({ -
- - {t("Cancel")} - - -
); }; @@ -102,15 +101,4 @@ const StyledModal = styled(Modal)` } `; -const Footer = styled.div` - position: absolute; - bottom: 20px; - right: 20px; - padding: 10px; -`; - -const CancelButton = styled(Button)` - margin-right: 8px; -`; - export default UploadModal; diff --git a/web/src/components/molecules/Content/Details/index.tsx b/web/src/components/molecules/Content/Details/index.tsx index ec6d385b88..02015d28f3 100644 --- a/web/src/components/molecules/Content/Details/index.tsx +++ b/web/src/components/molecules/Content/Details/index.tsx @@ -46,6 +46,7 @@ interface Props { totalCount: number; page: number; pageSize: number; + publishLoading: boolean; requestModalLoading: boolean; requestModalTotalCount: number; requestModalPage: number; @@ -140,6 +141,7 @@ const ContentDetailsMolecule: React.FC = ({ onRequestTableChange, onRequestSearchTerm, onRequestTableReload, + publishLoading, requestModalLoading, requestModalTotalCount, requestModalPage, @@ -213,6 +215,7 @@ const ContentDetailsMolecule: React.FC = ({ onRequestTableChange={onRequestTableChange} onRequestSearchTerm={onRequestSearchTerm} onRequestTableReload={onRequestTableReload} + publishLoading={publishLoading} requestModalLoading={requestModalLoading} requestModalTotalCount={requestModalTotalCount} requestModalPage={requestModalPage} diff --git a/web/src/components/molecules/Content/Form/index.tsx b/web/src/components/molecules/Content/Form/index.tsx index 544128f8d8..f5f9594703 100644 --- a/web/src/components/molecules/Content/Form/index.tsx +++ b/web/src/components/molecules/Content/Form/index.tsx @@ -64,6 +64,7 @@ interface Props { totalCount: number; page: number; pageSize: number; + publishLoading: boolean; requestModalLoading: boolean; requestModalTotalCount: number; requestModalPage: number; @@ -152,6 +153,7 @@ const ContentForm: React.FC = ({ onRequestTableChange, onRequestSearchTerm, onRequestTableReload, + publishLoading, requestModalLoading, requestModalTotalCount, requestModalPage, @@ -481,7 +483,7 @@ const ContentForm: React.FC = ({ {itemId && ( <> {showPublishAction && ( - )} @@ -664,9 +666,10 @@ const ContentForm: React.FC = ({ onRequestTableReload={onRequestTableReload} /> diff --git a/web/src/components/molecules/Content/List/index.tsx b/web/src/components/molecules/Content/List/index.tsx index 81fe097b58..321a303143 100644 --- a/web/src/components/molecules/Content/List/index.tsx +++ b/web/src/components/molecules/Content/List/index.tsx @@ -24,7 +24,9 @@ interface Props { collapsed: boolean; model?: Model; contentTableFields?: ContentTableField[]; - itemsDataLoading: boolean; + loading: boolean; + deleteLoading: boolean; + unpublishLoading: boolean; contentTableColumns?: ExtendedColumns[]; modelsMenu: React.ReactNode; selectedItem?: Item; @@ -70,7 +72,9 @@ const ContentListMolecule: React.FC = ({ contentTableFields, contentTableColumns, modelsMenu, - itemsDataLoading, + loading, + deleteLoading, + unpublishLoading, selectedItem, selection, totalCount, @@ -141,7 +145,9 @@ const ContentListMolecule: React.FC = ({ searchTerm={searchTerm} page={page} pageSize={pageSize} - loading={itemsDataLoading} + loading={loading} + deleteLoading={deleteLoading} + unpublishLoading={unpublishLoading} selectedItem={selectedItem} selection={selection} onUnpublish={onUnpublish} diff --git a/web/src/components/molecules/Content/PublishItemModal/index.tsx b/web/src/components/molecules/Content/PublishItemModal/index.tsx index e0445cdf61..933b7e8d9d 100644 --- a/web/src/components/molecules/Content/PublishItemModal/index.tsx +++ b/web/src/components/molecules/Content/PublishItemModal/index.tsx @@ -1,6 +1,7 @@ import styled from "@emotion/styled"; import { useCallback, useEffect, useState } from "react"; +import Button from "@reearth-cms/components/atoms/Button"; import Checkbox from "@reearth-cms/components/atoms/Checkbox"; import Form from "@reearth-cms/components/atoms/Form"; import Modal from "@reearth-cms/components/atoms/Modal"; @@ -17,6 +18,7 @@ interface FormValues { interface Props { open: boolean; + loading: boolean; itemId: string; unpublishedItems: FormItem[]; onClose: () => void; @@ -29,6 +31,7 @@ const initialValues: FormValues = { const PublishItemModal: React.FC = ({ open, + loading, itemId, unpublishedItems, onClose, @@ -72,8 +75,20 @@ const PublishItemModal: React.FC = ({ const handleClose = useCallback(() => { onClose(); }, [onClose]); + return ( - + + {t("Cancel")} + , + , + ]}>
{unpublishedItems?.length !== 0 && ( = ({ contentTableFields, contentTableColumns, loading, + deleteLoading, + unpublishLoading, selectedItem, selection, totalCount, @@ -173,16 +177,16 @@ const ContentTable: React.FC = ({ fieldType: "STATUS", key: "STATUS", render: (_, item) => { - const itemStatus: StateType[] = item.status.split("_") as StateType[]; + const itemStatus = item.status.split("_") as StateType[]; return ( <> - {itemStatus.map((state, index) => { - if (index === itemStatus.length - 1) { - return ; - } else { - return ; - } - })} + {itemStatus.map((state, index) => ( + + ))} ); }, @@ -286,27 +290,46 @@ const ContentTable: React.FC = ({ [selection, setSelection], ); - const AlertOptions = useCallback( + const alertOptions = useCallback( // eslint-disable-next-line @typescript-eslint/no-explicit-any (props: any) => { return ( - - onAddItemToRequestModalOpen()}> - {t("Add to Request")} - - onUnpublish(props.selectedRowKeys)}> - {t("Unpublish")} - - - {t("Deselect")} - - onItemDelete?.(props.selectedRowKeys)}> - {t("Delete")} - + + + + + ); }, - [onAddItemToRequestModalOpen, onItemDelete, onUnpublish, t], + [deleteLoading, onAddItemToRequestModalOpen, onItemDelete, onUnpublish, t, unpublishLoading], ); const defaultFilterValues = useRef([]); @@ -758,7 +781,7 @@ const ContentTable: React.FC = ({ toolbar={handleToolbarEvents} toolBarRender={toolBarRender} dataSource={contentTableFields} - tableAlertOptionRender={AlertOptions} + tableAlertOptionRender={alertOptions} rowSelection={rowSelection} columns={tableColumns} columnsState={{ @@ -816,19 +839,6 @@ const StyledButton = styled(Button)` padding: 0; `; -const PrimaryButton = styled.a` - display: flex; - align-items: center; - gap: 8px; -`; - -const DeleteButton = styled.a` - color: #ff7875; - :hover { - color: #ff7875b3; - } -`; - const StyledBadge = styled(Badge)` + * { margin-left: 4px; diff --git a/web/src/components/molecules/Content/Table/types.ts b/web/src/components/molecules/Content/Table/types.ts index 61d1846aa3..c9170412e5 100644 --- a/web/src/components/molecules/Content/Table/types.ts +++ b/web/src/components/molecules/Content/Table/types.ts @@ -11,9 +11,14 @@ import { MultipleOperator, } from "@reearth-cms/components/molecules/View/types"; import { Member } from "@reearth-cms/components/molecules/Workspace/types"; +import { t } from "@reearth-cms/i18n"; export type ColorType = "#BFBFBF" | "#52C41A" | "#FA8C16"; export type StateType = "DRAFT" | "PUBLIC" | "REVIEW"; +t("DRAFT"); +t("PUBLIC"); +t("REVIEW"); + export interface DefaultFilterValueType { operatorType: string; operator: Operator; diff --git a/web/src/components/molecules/Integration/IntegrationTable/index.tsx b/web/src/components/molecules/Integration/IntegrationTable/index.tsx index 80168c57e1..566614e1b3 100644 --- a/web/src/components/molecules/Integration/IntegrationTable/index.tsx +++ b/web/src/components/molecules/Integration/IntegrationTable/index.tsx @@ -25,6 +25,7 @@ interface Props { onSearchTerm: (term?: string) => void; onIntegrationSettingsModalOpen: (integrationMember: IntegrationMember) => void; setSelection: (input: { selectedRowKeys: Key[] }) => void; + deleteLoading: boolean; onIntegrationRemove: (integrationIds: string[]) => Promise; page: number; pageSize: number; @@ -40,6 +41,7 @@ const IntegrationTable: React.FC = ({ onSearchTerm, onIntegrationSettingsModalOpen, setSelection, + deleteLoading, onIntegrationRemove, page, pageSize, @@ -129,16 +131,26 @@ const IntegrationTable: React.FC = ({ const alertOptions = useCallback( // eslint-disable-next-line @typescript-eslint/no-explicit-any (props: any) => ( - - - {t("Deselect")} - - onIntegrationRemove(props.selectedRowKeys)}> - {t("Remove")} - + + + ), - [onIntegrationRemove, t], + [deleteLoading, onIntegrationRemove, t], ); const options = useMemo( @@ -230,19 +242,6 @@ const Title = styled.h1` color: #000; `; -const DeselectButton = styled.a` - display: flex; - align-items: center; - gap: 8px; -`; - -const DeleteButton = styled.a` - color: #ff7875; - :hover { - color: #ff7875b3; - } -`; - const StyledIcon = styled(Icon)` color: #1890ff; font-size: 18px; diff --git a/web/src/components/molecules/Member/MemberAddModal/index.tsx b/web/src/components/molecules/Member/MemberAddModal/index.tsx index 24bf463ed8..29d4524686 100644 --- a/web/src/components/molecules/Member/MemberAddModal/index.tsx +++ b/web/src/components/molecules/Member/MemberAddModal/index.tsx @@ -20,6 +20,7 @@ interface Props { open: boolean; searchedUser?: User & { isMember: boolean }; searchedUserList: User[]; + addLoading: boolean; onUserSearch: (nameOrEmail: string) => Promise; onUserAdd: () => void; onClose: () => void; @@ -37,6 +38,7 @@ const MemberAddModal: React.FC = ({ open, searchedUser, searchedUserList, + addLoading, onUserSearch, onUserAdd, onClose, @@ -66,28 +68,22 @@ const MemberAddModal: React.FC = ({ [changeSearchedUserList], ); - const handleSubmit = useCallback(() => { - form - .validateFields() - .then(() => { - if (searchedUserList.length > 0) { - onSubmit( - searchedUserList.map(user => { - return { - userId: user.id, - role: "READER", - }; - }), - ); - } - changeSearchedUser(undefined); - changeSearchedUserList([]); - onClose(); - form.resetFields(); - }) - .catch(info => { - console.log("Validate Failed:", info); - }); + const handleSubmit = useCallback(async () => { + if (searchedUserList.length === 0) return; + try { + await onSubmit( + searchedUserList.map(user => ({ + userId: user.id, + role: "READER", + })), + ); + changeSearchedUser(undefined); + changeSearchedUserList([]); + onClose(); + form.resetFields(); + } catch (error) { + console.error(error); + } }, [form, searchedUserList, changeSearchedUser, changeSearchedUserList, onClose, onSubmit]); const handleClose = useCallback(() => { @@ -102,13 +98,14 @@ const MemberAddModal: React.FC = ({ open={open} onCancel={handleClose} footer={[ - , , diff --git a/web/src/components/molecules/Member/MemberRoleModal/index.tsx b/web/src/components/molecules/Member/MemberRoleModal/index.tsx index b5fe2c0f50..859b16a5ff 100644 --- a/web/src/components/molecules/Member/MemberRoleModal/index.tsx +++ b/web/src/components/molecules/Member/MemberRoleModal/index.tsx @@ -1,5 +1,6 @@ -import React, { useCallback, useEffect } from "react"; +import React, { useCallback, useEffect, useState } from "react"; +import Button from "@reearth-cms/components/atoms/Button"; import Form from "@reearth-cms/components/atoms/Form"; import Modal from "@reearth-cms/components/atoms/Modal"; import Select from "@reearth-cms/components/atoms/Select"; @@ -14,14 +15,16 @@ interface FormValues { interface Props { open: boolean; member: UserMember; + loading: boolean; onClose: () => void; onSubmit: (userId: string, role: RoleUnion) => Promise; } -const MemberRoleModal: React.FC = ({ open, member, onClose, onSubmit }) => { +const MemberRoleModal: React.FC = ({ open, member, loading, onClose, onSubmit }) => { const t = useT(); const { Option } = Select; const [form] = Form.useForm(); + const [isDisabled, setIsDisabled] = useState(true); useEffect(() => { form.setFieldsValue({ @@ -29,17 +32,15 @@ const MemberRoleModal: React.FC = ({ open, member, onClose, onSubmit }) = }); }, [form, member]); - const handleSubmit = useCallback(() => { - form - .validateFields() - .then(async values => { - await onSubmit(member.userId, values.role); - onClose(); - form.resetFields(); - }) - .catch(info => { - console.log("Validate Failed:", info); - }); + const handleSubmit = useCallback(async () => { + const values = await form.validateFields(); + try { + await onSubmit(member.userId, values.role); + onClose(); + form.resetFields(); + } catch (error) { + console.error(error); + } }, [member, form, onSubmit, onClose]); const handleClose = useCallback(() => { @@ -47,8 +48,26 @@ const MemberRoleModal: React.FC = ({ open, member, onClose, onSubmit }) = onClose(); }, [form, onClose]); + const handleSelect = useCallback( + (value: string) => { + setIsDisabled(value === member.role); + }, + [member.role], + ); + return ( - + + {t("Cancel")} + , + , + ]}> = ({ open, member, onClose, onSubmit }) = message: t("Please input the appropriate role for this member!"), }, ]}> - diff --git a/web/src/components/molecules/Member/MemberTable/index.tsx b/web/src/components/molecules/Member/MemberTable/index.tsx index c067dbb28b..4575d17694 100644 --- a/web/src/components/molecules/Member/MemberTable/index.tsx +++ b/web/src/components/molecules/Member/MemberTable/index.tsx @@ -64,8 +64,8 @@ const MemberTable: React.FC = ({ content: t( "Remove this member from workspace means this member will not view any content of this workspace.", ), - onOk() { - handleMemberRemoveFromWorkspace(userIds); + async onOk() { + await handleMemberRemoveFromWorkspace(userIds); }, }); }, diff --git a/web/src/components/molecules/MyIntegrations/Settings/DangerZone.tsx b/web/src/components/molecules/MyIntegrations/Settings/DangerZone.tsx index 3e61a33e72..2e54c1143f 100644 --- a/web/src/components/molecules/MyIntegrations/Settings/DangerZone.tsx +++ b/web/src/components/molecules/MyIntegrations/Settings/DangerZone.tsx @@ -7,7 +7,7 @@ import ContentSection from "@reearth-cms/components/atoms/InnerContents/ContentS import Modal from "@reearth-cms/components/atoms/Modal"; import { useT } from "@reearth-cms/i18n"; -export interface Props { +interface Props { onIntegrationDelete: () => Promise; } @@ -26,8 +26,9 @@ const DangerZone: React.FC = ({ onIntegrationDelete }) => { {t("Once the integration is removed, it will disappear from all workspaces.")} ), - onOk() { - onIntegrationDelete(); + cancelText: t("Cancel"), + async onOk() { + await onIntegrationDelete(); }, }); }, [confirm, onIntegrationDelete, t]); @@ -40,7 +41,6 @@ const DangerZone: React.FC = ({ onIntegrationDelete }) => { "Permanently remove your Integration and all of its contents from the Re:Earth CMS. This action is not reversible – please continue with caution.", )} - diff --git a/web/src/components/molecules/MyIntegrations/Settings/Form.tsx b/web/src/components/molecules/MyIntegrations/Settings/Form.tsx index 615f8d254d..a627f2d618 100644 --- a/web/src/components/molecules/MyIntegrations/Settings/Form.tsx +++ b/web/src/components/molecules/MyIntegrations/Settings/Form.tsx @@ -76,6 +76,7 @@ const MyIntegrationForm: React.FC = ({ content: t( "If you regenerate the integration token, the previous token will become invalid, and this action cannot be undone. Are you sure you want to proceed?", ), + cancelText: t("Cancel"), okText: t("Reset"), onOk() { onRegenerateToken(); diff --git a/web/src/components/molecules/MyIntegrations/Webhook/WebhookList/WebhookCard/index.tsx b/web/src/components/molecules/MyIntegrations/Webhook/WebhookList/WebhookCard/index.tsx index 978df9d1d1..5aa2ada0a3 100644 --- a/web/src/components/molecules/MyIntegrations/Webhook/WebhookList/WebhookCard/index.tsx +++ b/web/src/components/molecules/MyIntegrations/Webhook/WebhookList/WebhookCard/index.tsx @@ -1,9 +1,11 @@ import styled from "@emotion/styled"; -import { Switch } from "antd"; -import { useCallback } from "react"; +import { useCallback, useState } from "react"; +import Button from "@reearth-cms/components/atoms/Button"; import Card from "@reearth-cms/components/atoms/Card"; import Icon from "@reearth-cms/components/atoms/Icon"; +import Space from "@reearth-cms/components/atoms/Space"; +import Switch from "@reearth-cms/components/atoms/Switch"; import { Webhook, WebhookTrigger } from "@reearth-cms/components/molecules/MyIntegrations/types"; interface Props { @@ -25,8 +27,15 @@ const WebhookCard: React.FC = ({ onWebhookUpdate, onWebhookSettings, }) => { - const handleWebhookDelete = useCallback(() => { - onWebhookDelete(webhook.id); + const [isLoading, setIsLoading] = useState(false); + + const handleWebhookDelete = useCallback(async () => { + setIsLoading(true); + try { + await onWebhookDelete(webhook.id); + } finally { + setIsLoading(false); + } }, [onWebhookDelete, webhook.id]); const handleWebhookUpdate = useCallback( @@ -50,10 +59,23 @@ const WebhookCard: React.FC = ({ } extra={ - <> - onWebhookSettings(webhook.id)} /> - - + + + ); }, - [onRequestDelete, t], + [deleteLoading, onRequestDelete, t], ); return ( void; onDelete: (modelId?: string) => Promise; isModel: boolean; } -const DeletionModal: React.FC = ({ open, data, onClose, onDelete, isModel }) => { +const DeletionModal: React.FC = ({ + open, + data, + deleteLoading, + onClose, + onDelete, + isModel, +}) => { const t = useT(); const title = useMemo(() => (isModel ? t("Delete Model") : t("Delete Group")), [isModel, t]); const confirmation = useMemo( @@ -42,10 +50,15 @@ const DeletionModal: React.FC = ({ open, data, onClose, onDelete, isModel open={open} onCancel={onClose} footer={[ - , - , ]}> diff --git a/web/src/components/molecules/Schema/ModelFieldList.tsx b/web/src/components/molecules/Schema/ModelFieldList.tsx index 15ca03a1be..ccafb1e074 100644 --- a/web/src/components/molecules/Schema/ModelFieldList.tsx +++ b/web/src/components/molecules/Schema/ModelFieldList.tsx @@ -2,6 +2,7 @@ import styled from "@emotion/styled"; import { useCallback, useEffect, useState } from "react"; import ReactDragListView from "react-drag-listview"; +import Button from "@reearth-cms/components/atoms/Button"; import Icon from "@reearth-cms/components/atoms/Icon"; import List from "@reearth-cms/components/atoms/List"; import Modal from "@reearth-cms/components/atoms/Modal"; @@ -34,8 +35,10 @@ const ModelFieldList: React.FC = ({ confirm({ title: t("Are you sure you want to delete this field?"), icon: , - onOk() { - onFieldDelete(fieldId); + cancelText: t("Cancel"), + maskClosable: true, + async onOk() { + await onFieldDelete(fieldId); }, }); }, @@ -109,15 +112,19 @@ const ModelFieldList: React.FC = ({ className="draggable-item" key={index} actions={[ - handleFieldDeleteConfirmation((item as Field).id)} - key="delete" + diff --git a/web/src/components/organisms/Project/Asset/AssetList/hooks.ts b/web/src/components/organisms/Project/Asset/AssetList/hooks.ts index ab57dfe2d6..87ce178502 100644 --- a/web/src/components/organisms/Project/Asset/AssetList/hooks.ts +++ b/web/src/components/organisms/Project/Asset/AssetList/hooks.ts @@ -46,14 +46,13 @@ export default (isItemsRequired: boolean) => { autoUnzip: true, }); const [uploadType, setUploadType] = useState("local"); - const [uploading, setUploading] = useState(false); const [collapsed, setCollapsed] = useState(true); const [page, setPage] = useState(location.state?.page ?? 1); const [pageSize, setPageSize] = useState(location.state?.pageSize ?? 10); const [searchTerm, setSearchTerm] = useState(location.state?.searchTerm ?? ""); - const [createAssetMutation] = useCreateAssetMutation(); - const [createAssetUploadMutation] = useCreateAssetUploadMutation(); + const [createAssetMutation, { loading: createLoading }] = useCreateAssetMutation(); + const [createAssetUploadMutation, { loading: uploadLoading }] = useCreateAssetUploadMutation(); const [sort, setSort] = useState<{ type?: AssetSortType; direction?: SortDirection } | undefined>( { @@ -109,83 +108,80 @@ export default (isItemsRequired: boolean) => { const handleUploadModalCancel = useCallback(() => { setUploadModalVisibility(false); - setUploading(false); setFileList([]); setUploadUrl({ url: "", autoUnzip: true }); setUploadType("local"); - }, [setUploadModalVisibility, setUploading, setFileList, setUploadUrl, setUploadType]); + }, [setUploadModalVisibility, setFileList, setUploadUrl, setUploadType]); const handleAssetsCreate = useCallback( - (files: UploadFile[]) => - (async () => { - if (!projectId) return []; - setUploading(true); - const results = ( - await Promise.all( - files.map(async file => { - let cursor = ""; - let offset = 0; - let uploadToken = ""; - while (true) { - const createAssetUploadResult = await createAssetUploadMutation({ - variables: { - projectId, - filename: file.name, - contentLength: file.size ?? 0, - cursor, - }, - }); - if ( - createAssetUploadResult.errors || - !createAssetUploadResult.data?.createAssetUpload - ) { - Notification.error({ message: t("Failed to add one or more assets.") }); - handleUploadModalCancel(); - return undefined; - } - const { url, token, contentType, contentLength, next } = - createAssetUploadResult.data.createAssetUpload; - uploadToken = token ?? ""; - if (url === "") { - break; - } - const headers = contentType ? { "content-type": contentType } : undefined; - await fetch(url, { - method: "PUT", - // eslint-disable-next-line @typescript-eslint/no-explicit-any - body: (file as any).slice(offset, offset + contentLength), - headers, - }); - if (!next) { - break; - } - cursor = next; - offset += contentLength; - } - const result = await createAssetMutation({ + async (files: UploadFile[]) => { + if (!projectId) return []; + const results = ( + await Promise.all( + files.map(async file => { + let cursor = ""; + let offset = 0; + let uploadToken = ""; + while (true) { + const createAssetUploadResult = await createAssetUploadMutation({ variables: { projectId, - token: uploadToken, - file: uploadToken === "" ? file : null, - skipDecompression: !!file.skipDecompression, + filename: file.name, + contentLength: file.size ?? 0, + cursor, }, }); - if (result.errors || !result.data?.createAsset) { + if ( + createAssetUploadResult.errors || + !createAssetUploadResult.data?.createAssetUpload + ) { Notification.error({ message: t("Failed to add one or more assets.") }); handleUploadModalCancel(); return undefined; } - return fromGraphQLAsset(result.data.createAsset.asset as GQLAsset); - }), - ) - ).filter(Boolean); - if (results?.length > 0) { - Notification.success({ message: t("Successfully added one or more assets!") }); - await refetch(); - } - handleUploadModalCancel(); - return results; - })(), + const { url, token, contentType, contentLength, next } = + createAssetUploadResult.data.createAssetUpload; + uploadToken = token ?? ""; + if (url === "") { + break; + } + const headers = contentType ? { "content-type": contentType } : undefined; + await fetch(url, { + method: "PUT", + // eslint-disable-next-line @typescript-eslint/no-explicit-any + body: (file as any).slice(offset, offset + contentLength), + headers, + }); + if (!next) { + break; + } + cursor = next; + offset += contentLength; + } + const result = await createAssetMutation({ + variables: { + projectId, + token: uploadToken, + file: uploadToken === "" ? file : null, + skipDecompression: !!file.skipDecompression, + }, + }); + if (result.errors || !result.data?.createAsset) { + Notification.error({ message: t("Failed to add one or more assets.") }); + handleUploadModalCancel(); + return undefined; + } + return fromGraphQLAsset(result.data.createAsset.asset as GQLAsset); + }), + ) + ).filter(Boolean); + if (results?.length > 0) { + Notification.success({ message: t("Successfully added one or more assets!") }); + await refetch(); + } + handleUploadModalCancel(); + return results; + }, [ projectId, handleUploadModalCancel, @@ -199,7 +195,6 @@ export default (isItemsRequired: boolean) => { const handleAssetCreateFromUrl = useCallback( async (url: string, autoUnzip: boolean) => { if (!projectId) return undefined; - setUploading(true); try { const result = await createAssetMutation({ variables: { @@ -224,27 +219,26 @@ export default (isItemsRequired: boolean) => { [projectId, createAssetMutation, t, refetch, handleUploadModalCancel], ); - const [deleteAssetMutation] = useDeleteAssetMutation(); + const [deleteAssetMutation, { loading: deleteLoading }] = useDeleteAssetMutation(); const handleAssetDelete = useCallback( - (assetIds: string[]) => - (async () => { - if (!projectId) return; - const results = await Promise.all( - assetIds.map(async assetId => { - const result = await deleteAssetMutation({ - variables: { assetId }, - }); - if (result.errors) { - Notification.error({ message: t("Failed to delete one or more assets.") }); - } - }), - ); - if (results) { - await refetch(); - Notification.success({ message: t("One or more assets were successfully deleted!") }); - setSelection({ selectedRowKeys: [] }); - } - })(), + async (assetIds: string[]) => { + if (!projectId) return; + const results = await Promise.all( + assetIds.map(async assetId => { + const result = await deleteAssetMutation({ + variables: { assetId }, + }); + if (result.errors) { + Notification.error({ message: t("Failed to delete one or more assets.") }); + } + }), + ); + if (results) { + await refetch(); + Notification.success({ message: t("One or more assets were successfully deleted!") }); + setSelection({ selectedRowKeys: [] }); + } + }, [t, deleteAssetMutation, refetch, projectId], ); @@ -316,9 +310,10 @@ export default (isItemsRequired: boolean) => { assetList, selection, fileList, - uploading, + uploading: createLoading || uploadLoading, uploadModalVisibility, loading, + deleteLoading, uploadUrl, uploadType, selectedAsset, @@ -336,7 +331,6 @@ export default (isItemsRequired: boolean) => { setUploadType, setSelection, setFileList, - setUploading, setUploadModalVisibility, handleAssetsCreate, handleAssetCreateFromUrl, diff --git a/web/src/components/organisms/Project/Asset/AssetList/index.tsx b/web/src/components/organisms/Project/Asset/AssetList/index.tsx index 96edd8ccb4..8b2f1da38b 100644 --- a/web/src/components/organisms/Project/Asset/AssetList/index.tsx +++ b/web/src/components/organisms/Project/Asset/AssetList/index.tsx @@ -13,6 +13,7 @@ const AssetList: React.FC = () => { uploading, uploadModalVisibility, loading, + deleteLoading, uploadUrl, uploadType, selectedAsset, @@ -66,6 +67,7 @@ const AssetList: React.FC = () => { uploading={uploading} uploadModalVisibility={uploadModalVisibility} loading={loading} + deleteLoading={deleteLoading} uploadUrl={uploadUrl} uploadType={uploadType} onAssetItemSelect={handleAssetItemSelect} diff --git a/web/src/components/organisms/Project/Content/ContentDetails/hooks.ts b/web/src/components/organisms/Project/Content/ContentDetails/hooks.ts index c23cec13b9..d88b29fb6e 100644 --- a/web/src/components/organisms/Project/Content/ContentDetails/hooks.ts +++ b/web/src/components/organisms/Project/Content/ContentDetails/hooks.ts @@ -60,6 +60,7 @@ export default () => { handleRequestSearchTerm, handleRequestTableReload, loading, + publishLoading, totalCount, page, pageSize, @@ -510,7 +511,7 @@ export default () => { [createRequestMutation, currentProject?.id, t], ); - const [updateRequestMutation] = useUpdateRequestMutation({ + const [updateRequestMutation, { loading: updateRequestLoading }] = useUpdateRequestMutation({ refetchQueries: ["GetRequests"], }); @@ -595,6 +596,7 @@ export default () => { handleRequestTableChange, handleRequestSearchTerm, handleRequestTableReload, + publishLoading, requestModalLoading: loading, requestModalTotalCount: totalCount, requestModalPage: page, @@ -610,6 +612,7 @@ export default () => { handleNavigateToModel, handleBack, handleRequestCreate, + updateRequestLoading, handleRequestUpdate, handleModalClose, handleModalOpen, diff --git a/web/src/components/organisms/Project/Content/ContentDetails/index.tsx b/web/src/components/organisms/Project/Content/ContentDetails/index.tsx index 8d3b98f8e4..5e05e157fb 100644 --- a/web/src/components/organisms/Project/Content/ContentDetails/index.tsx +++ b/web/src/components/organisms/Project/Content/ContentDetails/index.tsx @@ -39,6 +39,7 @@ const ContentDetails: React.FC = () => { handleRequestTableChange, handleRequestTableReload, handleRequestSearchTerm, + publishLoading, requestModalLoading, requestModalTotalCount, requestModalPage, @@ -105,10 +106,11 @@ const ContentDetails: React.FC = () => { onRequestTableChange={handleRequestTableChange} onRequestSearchTerm={handleRequestSearchTerm} onRequestTableReload={handleRequestTableReload} + publishLoading={publishLoading} + requestModalLoading={requestModalLoading} requestModalTotalCount={requestModalTotalCount} requestModalPage={requestModalPage} requestModalPageSize={requestModalPageSize} - requestModalLoading={requestModalLoading} collapsed={collapsedModelMenu} onCollapse={collapseModelMenu} commentsPanel={ diff --git a/web/src/components/organisms/Project/Content/ContentList/hooks.ts b/web/src/components/organisms/Project/Content/ContentList/hooks.ts index 0cd6b21a2a..2a7ff63788 100644 --- a/web/src/components/organisms/Project/Content/ContentList/hooks.ts +++ b/web/src/components/organisms/Project/Content/ContentList/hooks.ts @@ -67,6 +67,7 @@ export default () => { handleRequestSearchTerm, handleRequestTableReload, loading: requestModalLoading, + unpublishLoading, totalCount: requestModalTotalCount, page: requestModalPage, pageSize: requestModalPageSize, @@ -446,7 +447,7 @@ export default () => { ], ); - const [deleteItemMutation] = useDeleteItemMutation(); + const [deleteItemMutation, { loading: deleteLoading }] = useDeleteItemMutation(); const handleItemDelete = useCallback( (itemIds: string[]) => (async () => { @@ -520,6 +521,8 @@ export default () => { return { currentModel, loading, + deleteLoading, + unpublishLoading, contentTableFields, contentTableColumns, collapsedModelMenu, diff --git a/web/src/components/organisms/Project/Content/ContentList/index.tsx b/web/src/components/organisms/Project/Content/ContentList/index.tsx index 2310b037c0..fcb563b190 100644 --- a/web/src/components/organisms/Project/Content/ContentList/index.tsx +++ b/web/src/components/organisms/Project/Content/ContentList/index.tsx @@ -18,6 +18,8 @@ const ContentList: React.FC = () => { selectedItem, selection, loading, + deleteLoading, + unpublishLoading, totalCount, views, currentView, @@ -92,7 +94,9 @@ const ContentList: React.FC = () => { selectedItem={selectedItem} onItemSelect={handleItemSelect} collapsed={collapsedModelMenu} - itemsDataLoading={loading} + loading={loading} + deleteLoading={deleteLoading} + unpublishLoading={unpublishLoading} currentView={currentView} setCurrentView={setCurrentView} totalCount={totalCount} diff --git a/web/src/components/organisms/Project/Content/hooks.ts b/web/src/components/organisms/Project/Content/hooks.ts index b4a6a69b27..51539003c3 100644 --- a/web/src/components/organisms/Project/Content/hooks.ts +++ b/web/src/components/organisms/Project/Content/hooks.ts @@ -75,7 +75,7 @@ export default () => { [updateRequest, t], ); - const [publishItem] = usePublishItemMutation(); + const [publishItem, { loading: publishLoading }] = usePublishItemMutation(); const handlePublish = useCallback( async (itemIds: string[]) => { @@ -95,7 +95,7 @@ export default () => { [publishItem, t], ); - const [unpublishItem] = useUnpublishItemMutation(); + const [unpublishItem, { loading: unpublishLoading }] = useUnpublishItemMutation(); const handleUnpublish = useCallback( async (itemIds: string[]) => { @@ -157,6 +157,8 @@ export default () => { handleAddItemToRequestModalClose, handleAddItemToRequestModalOpen, loading, + publishLoading, + unpublishLoading, totalCount: data?.requests.totalCount ?? 0, page, pageSize, diff --git a/web/src/components/organisms/Project/Overview/hooks.ts b/web/src/components/organisms/Project/Overview/hooks.ts index 9f88645f9d..0ae7b6ea14 100644 --- a/web/src/components/organisms/Project/Overview/hooks.ts +++ b/web/src/components/organisms/Project/Overview/hooks.ts @@ -63,7 +63,7 @@ export default () => { setModelDeletionModalShown(false); }, [setSelectedModel, setModelDeletionModalShown]); - const [deleteModel] = useDeleteModelMutation({ + const [deleteModel, { loading: deleteLoading }] = useDeleteModelMutation({ refetchQueries: ["GetModels"], }); @@ -136,6 +136,7 @@ export default () => { modelModalShown, selectedModel, modelDeletionModalShown, + deleteLoading, handleSchemaNavigation, handleContentNavigation, handleModelKeyCheck, diff --git a/web/src/components/organisms/Project/Overview/index.tsx b/web/src/components/organisms/Project/Overview/index.tsx index 7cd765fc4d..d24b770af5 100644 --- a/web/src/components/organisms/Project/Overview/index.tsx +++ b/web/src/components/organisms/Project/Overview/index.tsx @@ -11,6 +11,7 @@ const ProjectOverview: React.FC = () => { modelModalShown, selectedModel, modelDeletionModalShown, + deleteLoading, handleSchemaNavigation, handleContentNavigation, handleModelKeyCheck, @@ -48,6 +49,7 @@ const ProjectOverview: React.FC = () => { { const location = useLocation(); const { data: userData } = useGetMeQuery(); - const { data: rawRequest, loading: requestLoading } = useGetRequestQuery({ + const { data: rawRequest, loading } = useGetRequestQuery({ variables: { requestId: requestId ?? "" }, skip: !requestId, fetchPolicy: "cache-and-network", @@ -78,7 +78,7 @@ export default () => { [currentRequest?.reviewers, currentRequest?.state, me?.id, myRole], ); - const [deleteRequestMutation] = useDeleteRequestMutation(); + const [deleteRequestMutation, { loading: deleteLoading }] = useDeleteRequestMutation(); const handleRequestDelete = useCallback( (requestsId: string[]) => (async () => { @@ -98,7 +98,7 @@ export default () => { [t, projectId, currentWorkspace?.id, navigate, deleteRequestMutation], ); - const [approveRequestMutation] = useApproveRequestMutation(); + const [approveRequestMutation, { loading: approveLoading }] = useApproveRequestMutation(); const handleRequestApprove = useCallback( (requestId: string) => (async () => { @@ -203,7 +203,9 @@ export default () => { return { me, isCloseActionEnabled, - loading: requestLoading, + loading, + deleteLoading, + approveLoading, isApproveActionEnabled, currentRequest, handleRequestDelete, diff --git a/web/src/components/organisms/Project/Request/RequestDetails/index.tsx b/web/src/components/organisms/Project/Request/RequestDetails/index.tsx index dbac70ed8e..1d8bd428fe 100644 --- a/web/src/components/organisms/Project/Request/RequestDetails/index.tsx +++ b/web/src/components/organisms/Project/Request/RequestDetails/index.tsx @@ -10,7 +10,9 @@ const RequestDetails: React.FC = () => { isCloseActionEnabled, isApproveActionEnabled, currentRequest, - loading: loadingRequest, + loading, + deleteLoading, + approveLoading, handleRequestApprove, handleRequestDelete, handleCommentCreate, @@ -21,7 +23,8 @@ const RequestDetails: React.FC = () => { const { handleGetAsset } = useAssetHooks(false); - const { workspaceUserMembers, handleRequestUpdate, handleGroupGet } = useContentHooks(); + const { workspaceUserMembers, updateRequestLoading, handleRequestUpdate, handleGroupGet } = + useContentHooks(); return ( { isApproveActionEnabled={isApproveActionEnabled} currentRequest={currentRequest} workspaceUserMembers={workspaceUserMembers} + deleteLoading={deleteLoading} + approveLoading={approveLoading} + updateLoading={updateRequestLoading} onRequestApprove={handleRequestApprove} onRequestUpdate={handleRequestUpdate} onRequestDelete={handleRequestDelete} @@ -37,7 +43,7 @@ const RequestDetails: React.FC = () => { onCommentUpdate={handleCommentUpdate} onCommentDelete={handleCommentDelete} onBack={handleNavigateToRequestsList} - loading={loadingRequest} + loading={loading} onGetAsset={handleGetAsset} onGroupGet={handleGroupGet} /> diff --git a/web/src/components/organisms/Project/Request/RequestList/hooks.ts b/web/src/components/organisms/Project/Request/RequestList/hooks.ts index 5dbf95cc85..e1c1a3fb4a 100644 --- a/web/src/components/organisms/Project/Request/RequestList/hooks.ts +++ b/web/src/components/organisms/Project/Request/RequestList/hooks.ts @@ -113,23 +113,22 @@ export default () => { [currentWorkspace?.id, navigate, page, pageSize, projectId, searchTerm], ); - const [deleteRequestMutation] = useDeleteRequestMutation(); + const [deleteRequestMutation, { loading: deleteLoading }] = useDeleteRequestMutation(); const handleRequestDelete = useCallback( - (requestsId: string[]) => - (async () => { - if (!projectId) return; - const result = await deleteRequestMutation({ - variables: { projectId, requestsId }, - refetchQueries: ["GetRequests"], - }); - if (result.errors) { - Notification.error({ message: t("Failed to delete one or more requests.") }); - } - if (result) { - Notification.success({ message: t("One or more requests were successfully closed!") }); - setSelection({ selectedRowKeys: [] }); - } - })(), + async (requestsId: string[]) => { + if (!projectId) return; + const result = await deleteRequestMutation({ + variables: { projectId, requestsId }, + refetchQueries: ["GetRequests"], + }); + if (result.errors) { + Notification.error({ message: t("Failed to delete one or more requests.") }); + } + if (result) { + Notification.success({ message: t("One or more requests were successfully closed!") }); + setSelection({ selectedRowKeys: [] }); + } + }, [t, projectId, deleteRequestMutation], ); @@ -171,6 +170,7 @@ export default () => { setSelection, handleRequestSelect, handleRequestsReload, + deleteLoading, handleRequestDelete, searchTerm, handleSearchTerm, diff --git a/web/src/components/organisms/Project/Request/RequestList/index.tsx b/web/src/components/organisms/Project/Request/RequestList/index.tsx index 747d45108a..8c412f61a2 100644 --- a/web/src/components/organisms/Project/Request/RequestList/index.tsx +++ b/web/src/components/organisms/Project/Request/RequestList/index.tsx @@ -17,6 +17,7 @@ const RequestList: React.FC = () => { collapseCommentsPanel, handleRequestSelect, handleRequestsReload, + deleteLoading, handleRequestDelete, searchTerm, handleSearchTerm, @@ -50,6 +51,7 @@ const RequestList: React.FC = () => { onRequestSelect={handleRequestSelect} loading={loading} onRequestsReload={handleRequestsReload} + deleteLoading={deleteLoading} onRequestDelete={handleRequestDelete} selectedRequest={selectedRequest} searchTerm={searchTerm} diff --git a/web/src/components/organisms/Project/Schema/hooks.ts b/web/src/components/organisms/Project/Schema/hooks.ts index 8a92c3febc..aa1e0e751e 100644 --- a/web/src/components/organisms/Project/Schema/hooks.ts +++ b/web/src/components/organisms/Project/Schema/hooks.ts @@ -282,7 +282,7 @@ export default () => { skip: !schemaId || selectedSchemaType !== "group", }); - const [deleteGroup] = useDeleteGroupMutation({ + const [deleteGroup, { loading: deleteGroupLoading }] = useDeleteGroupMutation({ refetchQueries: ["GetGroups"], }); @@ -384,8 +384,7 @@ export default () => { confirm({ title: t("No available Group"), content: t("Please create a Group first to use the field"), - okText: "Create Group", - okType: "primary", + okText: t("Create Group"), cancelText: t("Cancel"), onOk() { handleGroupModalOpen(); @@ -420,7 +419,7 @@ export default () => { [setModelDeletionModalShown], ); - const [deleteModel] = useDeleteModelMutation({ + const [deleteModel, { loading: deleteModelLoading }] = useDeleteModelMutation({ refetchQueries: ["GetModels"], }); @@ -547,6 +546,8 @@ export default () => { collapsed, fieldCreationLoading, fieldUpdateLoading, + deleteModelLoading, + deleteGroupLoading, setCollapsed, selectedSchemaType, handleModelSelect, diff --git a/web/src/components/organisms/Project/Schema/index.tsx b/web/src/components/organisms/Project/Schema/index.tsx index a5c5640aa4..811f72da40 100644 --- a/web/src/components/organisms/Project/Schema/index.tsx +++ b/web/src/components/organisms/Project/Schema/index.tsx @@ -49,6 +49,8 @@ const ProjectSchema: React.FC = () => { collapsed, fieldCreationLoading, fieldUpdateLoading, + deleteModelLoading, + deleteGroupLoading, setCollapsed, selectedSchemaType, handleModelSelect, @@ -112,6 +114,7 @@ const ProjectSchema: React.FC = () => { { [updateIntegrationToWorkspaceMutation, selectedIntegrationMember, workspaceId, refetch, t], ); - const [removeIntegrationFromWorkspaceMutation] = useRemoveIntegrationFromWorkspaceMutation(); + const [removeIntegrationFromWorkspaceMutation, { loading: deleteLoading }] = + useRemoveIntegrationFromWorkspaceMutation(); const handleIntegrationRemove = useCallback( async (integrationIds: string[]) => { @@ -174,6 +175,7 @@ export default (workspaceId?: string) => { handleIntegrationConnectModalOpen, addLoading, handleIntegrationConnect, + deleteLoading, handleIntegrationRemove, integrationConnectModalShown, handleUpdateIntegration, diff --git a/web/src/components/organisms/Settings/Integration/index.tsx b/web/src/components/organisms/Settings/Integration/index.tsx index 6346966fad..9839788f2f 100644 --- a/web/src/components/organisms/Settings/Integration/index.tsx +++ b/web/src/components/organisms/Settings/Integration/index.tsx @@ -16,6 +16,7 @@ const Integration: React.FC = () => { handleIntegrationConnectModalOpen, addLoading, handleIntegrationConnect, + deleteLoading, handleIntegrationRemove, integrationConnectModalShown, handleUpdateIntegration, @@ -43,6 +44,7 @@ const Integration: React.FC = () => { onIntegrationSettingsModalOpen={handleIntegrationSettingsModalOpen} onIntegrationConnectModalOpen={handleIntegrationConnectModalOpen} setSelection={setSelection} + deleteLoading={deleteLoading} onIntegrationRemove={handleIntegrationRemove} page={page} pageSize={pageSize} diff --git a/web/src/components/organisms/Settings/Members/hooks.ts b/web/src/components/organisms/Settings/Members/hooks.ts index 458ba096e8..e565ef4ef8 100644 --- a/web/src/components/organisms/Settings/Members/hooks.ts +++ b/web/src/components/organisms/Settings/Members/hooks.ts @@ -119,7 +119,7 @@ export default () => { [searchUserQuery, workspaceUserMembers], ); - const [addUsersToWorkspaceMutation] = useAddUsersToWorkspaceMutation(); + const [addUsersToWorkspaceMutation, { loading: addLoading }] = useAddUsersToWorkspaceMutation(); const handleUsersAddToWorkspace = useCallback( async (users: MemberInput[]) => { @@ -139,7 +139,8 @@ export default () => { [workspaceId, addUsersToWorkspaceMutation, setWorkspace, t], ); - const [updateMemberOfWorkspaceMutation] = useUpdateMemberOfWorkspaceMutation(); + const [updateMemberOfWorkspaceMutation, { loading: updateLoading }] = + useUpdateMemberOfWorkspaceMutation(); const handleMemberOfWorkspaceUpdate = useCallback( async (userId: string, role: RoleUnion) => { @@ -234,7 +235,9 @@ export default () => { changeSearchedUserList, handleUserSearch, handleUserAdd, + addLoading, handleUsersAddToWorkspace, + updateLoading, handleMemberOfWorkspaceUpdate, selectedMember, roleModalShown, diff --git a/web/src/components/organisms/Settings/Members/index.tsx b/web/src/components/organisms/Settings/Members/index.tsx index 4f0599f3eb..34b07d009c 100644 --- a/web/src/components/organisms/Settings/Members/index.tsx +++ b/web/src/components/organisms/Settings/Members/index.tsx @@ -15,7 +15,9 @@ const Members: React.FC = () => { changeSearchedUserList, handleUserSearch, handleUserAdd, + addLoading, handleUsersAddToWorkspace, + updateLoading, handleMemberOfWorkspaceUpdate, selectedMember, roleModalShown, @@ -57,6 +59,7 @@ const Members: React.FC = () => { @@ -65,6 +68,7 @@ const Members: React.FC = () => { open={MemberAddModalShown} searchedUser={searchedUser} searchedUserList={searchedUserList} + addLoading={addLoading} onUserSearch={handleUserSearch} onUserAdd={handleUserAdd} onClose={handleMemberAddModalClose} diff --git a/web/src/components/organisms/Settings/MyIntegrationDetails/hooks.ts b/web/src/components/organisms/Settings/MyIntegrationDetails/hooks.ts index 6d5e5a3c41..d20aa84d6d 100644 --- a/web/src/components/organisms/Settings/MyIntegrationDetails/hooks.ts +++ b/web/src/components/organisms/Settings/MyIntegrationDetails/hooks.ts @@ -1,5 +1,5 @@ import { useCallback, useMemo, useState } from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import Notification from "@reearth-cms/components/atoms/Notification"; import { WebhookTrigger } from "@reearth-cms/components/molecules/MyIntegrations/types"; @@ -15,20 +15,18 @@ import { import { useT } from "@reearth-cms/i18n"; import { useWorkspace } from "@reearth-cms/state"; -interface Params { - integrationId?: string; -} - -export default ({ integrationId }: Params) => { +export default () => { + const { workspaceId, integrationId } = useParams(); const navigate = useNavigate(); const { integrations } = integrationHooks(); const t = useT(); const [webhookId, setwebhookId] = useState(); const [currentWorkspace] = useWorkspace(); - const selectedIntegration = useMemo(() => { - return integrations?.find(integration => integration.id === integrationId); - }, [integrations, integrationId]); + const selectedIntegration = useMemo( + () => integrations?.find(integration => integration.id === integrationId), + [integrations, integrationId], + ); const webhookInitialValues = useMemo(() => { if (!selectedIntegration?.config.webhooks || !webhookId) return; @@ -196,6 +194,10 @@ export default ({ integrationId }: Params) => { [setwebhookId], ); + const handleIntegrationHeaderBack = useCallback(() => { + navigate(`/workspace/${workspaceId}/myIntegrations`); + }, [navigate, workspaceId]); + return { integrations, selectedIntegration, @@ -211,5 +213,6 @@ export default ({ integrationId }: Params) => { handleWebhookDelete, handleWebhookUpdate, handleWebhookSelect, + handleIntegrationHeaderBack, }; }; diff --git a/web/src/components/organisms/Settings/MyIntegrationDetails/index.tsx b/web/src/components/organisms/Settings/MyIntegrationDetails/index.tsx index 486d76c0e0..e2160629b2 100644 --- a/web/src/components/organisms/Settings/MyIntegrationDetails/index.tsx +++ b/web/src/components/organisms/Settings/MyIntegrationDetails/index.tsx @@ -1,18 +1,8 @@ -import { useCallback } from "react"; -import { useNavigate, useParams } from "react-router-dom"; - import MyIntegrationContent from "@reearth-cms/components/molecules/MyIntegrations/Content"; import useHooks from "./hooks"; const MyIntegrationDetails: React.FC = () => { - const { workspaceId, integrationId } = useParams(); - const navigate = useNavigate(); - - const handleIntegrationHeaderBack = useCallback(() => { - navigate(`/workspace/${workspaceId}/myIntegrations`); - }, [navigate, workspaceId]); - const { selectedIntegration, webhookInitialValues, @@ -27,9 +17,8 @@ const MyIntegrationDetails: React.FC = () => { handleWebhookDelete, handleWebhookUpdate, handleWebhookSelect, - } = useHooks({ - integrationId, - }); + handleIntegrationHeaderBack, + } = useHooks(); return selectedIntegration ? ( { onWebhookCreate={handleWebhookCreate} onWebhookDelete={handleWebhookDelete} onWebhookUpdate={handleWebhookUpdate} - onIntegrationHeaderBack={handleIntegrationHeaderBack} onWebhookSelect={handleWebhookSelect} + onIntegrationHeaderBack={handleIntegrationHeaderBack} /> ) : null; }; diff --git a/web/src/i18n/index.tsx b/web/src/i18n/index.tsx index d1b8113688..a5817a8945 100644 --- a/web/src/i18n/index.tsx +++ b/web/src/i18n/index.tsx @@ -1,5 +1,6 @@ import { useTranslation } from "react-i18next"; +export { t } from "i18next"; export { Trans } from "react-i18next"; export { default as Provider } from "./provider"; export { localesWithLabel } from "./locale"; diff --git a/web/src/i18n/translations/en.yml b/web/src/i18n/translations/en.yml index afcba5e7ef..3aefeed1bc 100644 --- a/web/src/i18n/translations/en.yml +++ b/web/src/i18n/translations/en.yml @@ -16,6 +16,7 @@ Choose the scope of your project. This affects all the models shown below that a Project Alias: '' Save changes: '' Are you sure you want to delete your account?: '' +Cancel: '' Danger Zone: '' Delete Personal Account: '' Permanently removes your personal account and all of its contents from Re:Earth CMS. This action is not reversible, so please continue with caution.: '' @@ -71,13 +72,12 @@ Auto Unzip: '' Remove file: '' Click or drag files to this area to upload: '' Single or multiple file upload is supported: '' -Asset Uploader: '' -Local: '' -URL: '' -Cancel: '' Uploading: '' Upload and Link: '' Upload: '' +Asset Uploader: '' +Local: '' +URL: '' Please input the URL of the asset!: '' Please input a valid URL: '' Could not display svg: '' @@ -167,6 +167,9 @@ Filter: '' Add Filter: '' Add Sort: '' Control: '' +DRAFT: '' +PUBLIC: '' +REVIEW: '' Connect Integration: '' Connect: '' Integration Setting: '' @@ -256,7 +259,6 @@ Approve: '' Approved: '' Closed: '' Assign to: '' -DRAFT: '' APPROVED: '' CLOSED: '' WAITING: '' @@ -486,6 +488,7 @@ Failed to update group.: '' Successfully updated group!: '' No available Group: '' Please create a Group first to use the field: '' +Create Group: '' Failed to update project.: '' Successfully updated project!: '' Failed to update request roles.: '' diff --git a/web/src/i18n/translations/ja.yml b/web/src/i18n/translations/ja.yml index 0d0473c04d..a610c417c6 100644 --- a/web/src/i18n/translations/ja.yml +++ b/web/src/i18n/translations/ja.yml @@ -11,11 +11,12 @@ Assets: アセット Private: 非公開 Public: 公開 Accessibility: 公開設定 -Public Scope: 公開 +Public Scope: 公開範囲 Choose the scope of your project. This affects all the models shown below that are switched on.: プロジェクトの公開範囲を選択してください。この操作は以下のすべてのモデルに影響します。 Project Alias: プロジェクトエイリアス Save changes: 変更を保存 Are you sure you want to delete your account?: 本当にアカウントを削除してよろしいですか? +Cancel: キャンセル Danger Zone: 重要操作 Delete Personal Account: アカウントを削除 Permanently removes your personal account and all of its contents from Re:Earth CMS. This action is not reversible, so please continue with caution.: アカウントとそれに関連するすべてのコンテンツを削除します。この操作は取り消すことができません。よろしいですか? @@ -71,13 +72,12 @@ Auto Unzip: 自動解凍 Remove file: ファイルを削除 Click or drag files to this area to upload: このエリアにファイルをクリックまたはドラッグしてアップロードします Single or multiple file upload is supported: 複数のファイルのアップロードが可能です。 -Asset Uploader: アセットアップローダー -Local: ローカル -URL: '' -Cancel: キャンセル Uploading: アップロード中 Upload and Link: アップロードとリンク Upload: アップロード +Asset Uploader: アセットアップローダー +Local: ローカル +URL: '' Please input the URL of the asset!: アセットのURLを入力してください! Please input a valid URL: 有効なURLを入力してください Could not display svg: svgを表示できませんでした。 @@ -128,7 +128,7 @@ Item Information: アイテムの情報 Updated At: 更新日時 Updated By: 更新者 Publish State: 公開状態 -This item has been referenced: このアイテムは参照されました +This item has been referenced: このアイテムは参照されています Are you going to refer to it? The previous reference will be canceled automatically: 参照しますか?以前の参照は自動的に解除されますがよろしいですか? State: ステータス Reviewers: レビュワー @@ -167,6 +167,9 @@ Filter: フィルター Add Filter: 絞り込む Add Sort: 並び替える Control: 操作 +DRAFT: ドラフト +PUBLIC: 公開 +REVIEW: レビュー Connect Integration: インテグレーションを連携 Connect: 連携 Integration Setting: インテグレーション設定 @@ -256,7 +259,6 @@ Approve: 承認 Approved: 承認済み Closed: クローズ済み Assign to: 割り当てる -DRAFT: ドラフト APPROVED: 承認済 CLOSED: クローズ済 WAITING: レビュー待ち @@ -486,6 +488,7 @@ Failed to update group.: グループの更新に失敗しました。 Successfully updated group!: グループの更新に成功しました! No available Group: グループなし Please create a Group first to use the field: フィールドを使用するには、まずグループを作成してください。 +Create Group: グループを作成 Failed to update project.: プロジェクトの更新に失敗しました。 Successfully updated project!: プロジェクトの更新に成功しました。 Failed to update request roles.: リクエストロールの更新に失敗しました。