You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by mi...@apache.org on 2023/09/11 10:02:13 UTC

[incubator-devlake] branch feat-5791 created (now 47202092d)

This is an automated email from the ASF dual-hosted git repository.

mintsweet pushed a change to branch feat-5791
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


      at 47202092d feat(config-ui): improve the api key in webhook

This branch includes the following new commits:

     new 47202092d feat(config-ui): improve the api key in webhook

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[incubator-devlake] 01/01: feat(config-ui): improve the api key in webhook

Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

mintsweet pushed a commit to branch feat-5791
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git

commit 47202092d153007d3c9bf5649c92a37b9c3213c7
Author: mintsweet <0x...@gmail.com>
AuthorDate: Mon Sep 11 22:00:24 2023 +1200

    feat(config-ui): improve the api key in webhook
---
 .../index.tsx => components/delete-dialog.tsx}     | 44 +++++-----
 .../register/webook/components/edit-dialog.tsx     | 73 ++++++++++++++++
 .../plugins/register/webook/components/index.ts    |  3 +
 .../register/webook/components/view-dialog.tsx     | 98 ++++++++++++++++++++++
 .../plugins/register/webook/connection/index.tsx   | 29 ++-----
 .../register/webook/delete-dialog/styled.ts        | 21 -----
 .../register/webook/delete-dialog/use-delete.ts    | 50 -----------
 config-ui/src/plugins/register/webook/index.ts     |  2 -
 .../webook/{view-or-edit-dialog => }/styled.ts     | 21 +++--
 .../register/webook/view-or-edit-dialog/index.tsx  | 81 ------------------
 .../webook/view-or-edit-dialog/use-view-or-edit.ts | 77 -----------------
 11 files changed, 218 insertions(+), 281 deletions(-)

diff --git a/config-ui/src/plugins/register/webook/delete-dialog/index.tsx b/config-ui/src/plugins/register/webook/components/delete-dialog.tsx
similarity index 56%
rename from config-ui/src/plugins/register/webook/delete-dialog/index.tsx
rename to config-ui/src/plugins/register/webook/components/delete-dialog.tsx
index 4fbe0433b..b5d7698bc 100644
--- a/config-ui/src/plugins/register/webook/delete-dialog/index.tsx
+++ b/config-ui/src/plugins/register/webook/components/delete-dialog.tsx
@@ -16,40 +16,46 @@
  *
  */
 
-import { Dialog } from '@/components';
+import { useState } from 'react';
 
-import type { UseDeleteProps } from './use-delete';
-import { useDelete } from './use-delete';
-import * as S from './styled';
+import { Dialog, Message } from '@/components';
+import { useConnections } from '@/hooks';
+import { operator } from '@/utils';
 
-interface Props extends UseDeleteProps {
-  isOpen: boolean;
+import * as API from '../api';
+
+interface Props {
+  initialId: ID;
   onCancel: () => void;
 }
 
-export const WebhookDeleteDialog = ({ isOpen, onCancel, ...props }: Props) => {
-  const { saving, onSubmit } = useDelete({ ...props });
+export const DeleteDialog = ({ initialId, onCancel }: Props) => {
+  const [operating, setOperating] = useState(false);
+
+  const { onRefresh } = useConnections();
+
+  const handleSubmit = async () => {
+    const [success] = await operator(() => API.deleteConnection(initialId), {
+      setOperating,
+    });
 
-  const handleSubmit = () => {
-    onSubmit();
-    onCancel();
+    if (success) {
+      onRefresh('webhook');
+      onCancel();
+    }
   };
 
   return (
     <Dialog
-      isOpen={isOpen}
+      isOpen
       title="Delete this Webhook?"
-      style={{ width: 600 }}
+      // style={{ width: 600 }}
       okText="Confirm"
-      okLoading={saving}
+      okLoading={operating}
       onCancel={onCancel}
       onOk={handleSubmit}
     >
-      <S.Wrapper>
-        <div className="message">
-          <p>This Webhook cannot be recovered once it’s deleted.</p>
-        </div>
-      </S.Wrapper>
+      <Message content="This Webhook cannot be recovered once it’s deleted." />
     </Dialog>
   );
 };
diff --git a/config-ui/src/plugins/register/webook/components/edit-dialog.tsx b/config-ui/src/plugins/register/webook/components/edit-dialog.tsx
new file mode 100644
index 000000000..e66b554d1
--- /dev/null
+++ b/config-ui/src/plugins/register/webook/components/edit-dialog.tsx
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { useState, useEffect } from 'react';
+import { InputGroup } from '@blueprintjs/core';
+
+import { Dialog, FormItem } from '@/components';
+import { useConnections } from '@/hooks';
+import { operator } from '@/utils';
+
+import * as API from '../api';
+
+interface Props {
+  initialId: ID;
+  onCancel: () => void;
+}
+
+export const EditDialog = ({ initialId, onCancel }: Props) => {
+  const [name, setName] = useState('');
+  const [operating, setOperating] = useState(false);
+
+  const { onRefresh } = useConnections({ plugin: 'webhook' });
+
+  useEffect(() => {
+    (async () => {
+      const res = await API.getConnection(initialId);
+      setName(res.name);
+    })();
+  }, [initialId]);
+
+  const handleSubmit = async () => {
+    const [success] = await operator(() => API.updateConnection(initialId, { name }), {
+      setOperating,
+    });
+
+    if (success) {
+      onRefresh('webhook');
+      onCancel();
+    }
+  };
+
+  return (
+    <Dialog
+      style={{ width: 820 }}
+      isOpen
+      title="Edit Webhook Name"
+      okLoading={operating}
+      okDisabled={!name}
+      okText="Save"
+      onCancel={onCancel}
+      onOk={handleSubmit}
+    >
+      <FormItem label="Name" required>
+        <InputGroup value={name} onChange={(e) => setName(e.target.value)} />
+      </FormItem>
+    </Dialog>
+  );
+};
diff --git a/config-ui/src/plugins/register/webook/components/index.ts b/config-ui/src/plugins/register/webook/components/index.ts
index 30c0d1fa0..e3bf865f2 100644
--- a/config-ui/src/plugins/register/webook/components/index.ts
+++ b/config-ui/src/plugins/register/webook/components/index.ts
@@ -17,3 +17,6 @@
  */
 
 export * from './miller-columns';
+export * from './delete-dialog';
+export * from './edit-dialog';
+export * from './view-dialog';
diff --git a/config-ui/src/plugins/register/webook/components/view-dialog.tsx b/config-ui/src/plugins/register/webook/components/view-dialog.tsx
new file mode 100644
index 000000000..2c37ee6d9
--- /dev/null
+++ b/config-ui/src/plugins/register/webook/components/view-dialog.tsx
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { useState, useEffect, useMemo } from 'react';
+import { Button, Intent } from '@blueprintjs/core';
+
+import { Dialog, FormItem, CopyText } from '@/components';
+
+import * as API from '../api';
+import * as S from '../styled';
+
+interface Props {
+  initialId: ID;
+  onCancel: () => void;
+}
+
+export const ViewDialog = ({ initialId, onCancel }: Props) => {
+  const [record, setRecord] = useState({
+    postIssuesEndpoint: '',
+    closeIssuesEndpoint: '',
+    postDeploymentsCurl: '',
+  });
+  const [operating, setOperating] = useState();
+  const [apiKey, setApiKey] = useState('');
+
+  const prefix = useMemo(() => `${window.location.origin}/api`, []);
+
+  useEffect(() => {
+    (async () => {
+      const res = await API.getConnection(initialId);
+      setRecord({
+        postIssuesEndpoint: `${prefix}${res.postIssuesEndpoint}`,
+        closeIssuesEndpoint: `${prefix}${res.closeIssuesEndpoint}`,
+        postDeploymentsCurl: `curl ${prefix}${res.postPipelineDeployTaskEndpoint} -X 'POST' -d "{
+      \\"commit_sha\\":\\"the sha of deployment commit\\",
+      \\"repo_url\\":\\"the repo URL of the deployment commit\\",
+      \\"start_time\\":\\"Optional, eg. 2020-01-01T12:00:00+00:00\\"
+      }"`,
+      });
+    })();
+  }, [initialId]);
+
+  const handleGenerateNewKey = () => {};
+
+  return (
+    <Dialog style={{ width: 820 }} isOpen title="View Webhook" footer={null} onCancel={onCancel}>
+      <S.ViewWrapper>
+        <p>
+          Copy the following POST URLs to your issue tracking or CI tools to push `Incidents` and `Deployments` by
+          making a POST to DevLake. Please replace the API_key in the following URLs.
+        </p>
+        <FormItem label="Incidents">
+          <h5>Post to register an incident</h5>
+          <CopyText content={record.postIssuesEndpoint} />
+          <h5>Post to close a registered incident</h5>
+          <CopyText content={record.closeIssuesEndpoint} />
+        </FormItem>
+        <FormItem label="Deployment">
+          <h5>Post to register a deployment</h5>
+          <CopyText content={record.postDeploymentsCurl} />
+        </FormItem>
+        <FormItem
+          label="API Key"
+          subLabel="If you have forgotten your API key, you can revoke the previous key and generate a new one as a replacement."
+        >
+          {!apiKey ? (
+            <Button intent={Intent.PRIMARY} text="Revoke and generate a new key" onClick={handleGenerateNewKey} />
+          ) : (
+            <>
+              <S.ApiKey>
+                <CopyText content={apiKey} />
+                <span>No Expiration</span>
+              </S.ApiKey>
+              <S.Tips>
+                <strong>Please copy your key now. You will not be able to see it again.</strong>
+              </S.Tips>
+            </>
+          )}
+        </FormItem>
+      </S.ViewWrapper>
+    </Dialog>
+  );
+};
diff --git a/config-ui/src/plugins/register/webook/connection/index.tsx b/config-ui/src/plugins/register/webook/connection/index.tsx
index 3f0802e1b..afc1cacc9 100644
--- a/config-ui/src/plugins/register/webook/connection/index.tsx
+++ b/config-ui/src/plugins/register/webook/connection/index.tsx
@@ -24,9 +24,8 @@ import { useConnections } from '@/hooks';
 import { DOC_URL } from '@/release';
 
 import type { WebhookItemType } from '../types';
+import { ViewDialog, EditDialog, DeleteDialog } from '../components';
 import { WebhookCreateDialog } from '../create-dialog';
-import { WebhookDeleteDialog } from '../delete-dialog';
-import { WebhookViewOrEditDialog } from '../view-or-edit-dialog';
 
 import * as S from './styled';
 
@@ -73,7 +72,8 @@ export const WebHookConnection = ({ filterIds, onCreateAfter, onDeleteAfter }: P
       align: 'center',
       render: (_, row) => (
         <S.Action>
-          <IconButton icon="edit" tooltip="Edit" onClick={() => handleShowDialog('edit', row)} />
+          <IconButton icon="array" tooltip="View" onClick={() => handleShowDialog('show', row)} />
+          <IconButton icon="annotation" tooltip="Edit" onClick={() => handleShowDialog('edit', row)} />
           <IconButton icon="trash" tooltip="Delete" onClick={() => handleShowDialog('delete', row)} />
         </S.Action>
       ),
@@ -109,26 +109,9 @@ export const WebHookConnection = ({ filterIds, onCreateAfter, onDeleteAfter }: P
           }}
         />
       )}
-      {type === 'delete' && currentID && (
-        <WebhookDeleteDialog
-          isOpen
-          initialID={currentID}
-          onCancel={handleHideDialog}
-          onSubmitAfter={(id) => {
-            onRefresh();
-            onDeleteAfter?.(id);
-          }}
-        />
-      )}
-      {(type === 'edit' || type === 'show') && currentID && (
-        <WebhookViewOrEditDialog
-          type={type}
-          isOpen
-          initialID={currentID}
-          onCancel={handleHideDialog}
-          onSubmitAfter={() => onRefresh()}
-        />
-      )}
+      {type === 'show' && currentID && <ViewDialog initialId={currentID} onCancel={handleHideDialog} />}
+      {type === 'edit' && currentID && <EditDialog initialId={currentID} onCancel={handleHideDialog} />}
+      {type === 'delete' && currentID && <DeleteDialog initialId={currentID} onCancel={handleHideDialog} />}
     </S.Wrapper>
   );
 };
diff --git a/config-ui/src/plugins/register/webook/delete-dialog/styled.ts b/config-ui/src/plugins/register/webook/delete-dialog/styled.ts
deleted file mode 100644
index 4bf74a53c..000000000
--- a/config-ui/src/plugins/register/webook/delete-dialog/styled.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-import styled from 'styled-components';
-
-export const Wrapper = styled.div``;
diff --git a/config-ui/src/plugins/register/webook/delete-dialog/use-delete.ts b/config-ui/src/plugins/register/webook/delete-dialog/use-delete.ts
deleted file mode 100644
index 71d959f06..000000000
--- a/config-ui/src/plugins/register/webook/delete-dialog/use-delete.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-import { useState, useMemo } from 'react';
-
-import { operator } from '@/utils';
-
-import * as API from '../api';
-
-export interface UseDeleteProps {
-  initialID: ID;
-  onSubmitAfter?: (id: ID) => void;
-}
-
-export const useDelete = ({ initialID, onSubmitAfter }: UseDeleteProps) => {
-  const [saving, setSaving] = useState(false);
-
-  const handleDelete = async () => {
-    const [success] = await operator(() => API.deleteConnection(initialID), {
-      setOperating: setSaving,
-    });
-
-    if (success) {
-      onSubmitAfter?.(initialID);
-    }
-  };
-
-  return useMemo(
-    () => ({
-      saving,
-      onSubmit: handleDelete,
-    }),
-    [saving],
-  );
-};
diff --git a/config-ui/src/plugins/register/webook/index.ts b/config-ui/src/plugins/register/webook/index.ts
index fa62a00e3..708ebd02c 100644
--- a/config-ui/src/plugins/register/webook/index.ts
+++ b/config-ui/src/plugins/register/webook/index.ts
@@ -20,6 +20,4 @@ export * from './types';
 export * from './config';
 export * from './connection';
 export * from './create-dialog';
-export * from './delete-dialog';
-export * from './view-or-edit-dialog';
 export * from './selector-dialog';
diff --git a/config-ui/src/plugins/register/webook/view-or-edit-dialog/styled.ts b/config-ui/src/plugins/register/webook/styled.ts
similarity index 79%
rename from config-ui/src/plugins/register/webook/view-or-edit-dialog/styled.ts
rename to config-ui/src/plugins/register/webook/styled.ts
index 969380e28..d032f8282 100644
--- a/config-ui/src/plugins/register/webook/view-or-edit-dialog/styled.ts
+++ b/config-ui/src/plugins/register/webook/styled.ts
@@ -18,17 +18,22 @@
 
 import styled from 'styled-components';
 
-export const Wrapper = styled.div`
+export const ViewWrapper = styled.div`
   h5 {
     margin: 8px 0;
   }
+`;
+
+export const ApiKey = styled.div`
+  display: flex;
+  align-items: center;
 
-  .block {
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    margin-bottom: 8px;
-    padding: 10px 16px;
-    background: #f0f4fe;
+  & > div {
+    flex: 0 0 300px;
+    margin-right: 8px;
   }
 `;
+
+export const Tips = styled.div`
+  margin-top: 8px;
+`;
diff --git a/config-ui/src/plugins/register/webook/view-or-edit-dialog/index.tsx b/config-ui/src/plugins/register/webook/view-or-edit-dialog/index.tsx
deleted file mode 100644
index 1b9437b4f..000000000
--- a/config-ui/src/plugins/register/webook/view-or-edit-dialog/index.tsx
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-import { InputGroup } from '@blueprintjs/core';
-
-import { Dialog, FormItem, CopyText } from '@/components';
-
-import type { UseViewOrEditProps } from './use-view-or-edit';
-import { useViewOrEdit } from './use-view-or-edit';
-import * as S from './styled';
-
-interface Props extends UseViewOrEditProps {
-  type: 'edit' | 'show';
-  isOpen: boolean;
-  onCancel: () => void;
-}
-
-export const WebhookViewOrEditDialog = ({ type, isOpen, onCancel, ...props }: Props) => {
-  const { saving, name, record, onChangeName, onSubmit } = useViewOrEdit({
-    ...props,
-  });
-
-  const handleSubmit = () => {
-    if (type === 'edit') {
-      onSubmit();
-    }
-
-    onCancel();
-  };
-
-  return (
-    <Dialog
-      isOpen={isOpen}
-      title="View/Edit Webhook"
-      style={{ width: 820 }}
-      okText={type === 'edit' ? 'Save' : 'Done'}
-      okDisabled={!name}
-      okLoading={saving}
-      onCancel={onCancel}
-      onOk={handleSubmit}
-    >
-      <S.Wrapper>
-        <h3>Webhook Name *</h3>
-        <p>
-          Copy the following POST URLs to your issue tracking or CI tools to push `Incidents` and `Deployments` by
-          making a POST to DevLake.
-        </p>
-        <InputGroup disabled={type !== 'edit'} value={name} onChange={(e) => onChangeName(e.target.value)} />
-        <p>
-          Copy the following URLs to your issue tracking tool for Incidents and CI tool for Deployments by making a POST
-          to DevLake.
-        </p>
-        <FormItem label="Incidents">
-          <h5>Post to register an incident</h5>
-          <CopyText content={record.postIssuesEndpoint} />
-          <h5>Post to close a registered incident</h5>
-          <CopyText content={record.closeIssuesEndpoint} />
-        </FormItem>
-        <FormItem label="Deployment">
-          <h5>Post to register a deployment</h5>
-          <CopyText content={record.postDeploymentsCurl} />
-        </FormItem>
-      </S.Wrapper>
-    </Dialog>
-  );
-};
diff --git a/config-ui/src/plugins/register/webook/view-or-edit-dialog/use-view-or-edit.ts b/config-ui/src/plugins/register/webook/view-or-edit-dialog/use-view-or-edit.ts
deleted file mode 100644
index e7d14b18c..000000000
--- a/config-ui/src/plugins/register/webook/view-or-edit-dialog/use-view-or-edit.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-import { useState, useMemo, useEffect } from 'react';
-
-import { operator } from '@/utils';
-
-import * as API from '../api';
-
-export interface UseViewOrEditProps {
-  initialID: ID;
-  onSubmitAfter?: (id: ID) => void;
-}
-
-export const useViewOrEdit = ({ initialID, onSubmitAfter }: UseViewOrEditProps) => {
-  const [saving, setSaving] = useState(false);
-  const [name, setName] = useState('');
-  const [record, setRecord] = useState({
-    postIssuesEndpoint: '',
-    closeIssuesEndpoint: '',
-    postDeploymentsCurl: '',
-  });
-
-  const prefix = useMemo(() => `${window.location.origin}/api`, []);
-
-  useEffect(() => {
-    (async () => {
-      const res = await API.getConnection(initialID);
-      setName(res.name);
-      setRecord({
-        postIssuesEndpoint: `${prefix}${res.postIssuesEndpoint}`,
-        closeIssuesEndpoint: `${prefix}${res.closeIssuesEndpoint}`,
-        postDeploymentsCurl: `curl ${prefix}${res.postPipelineDeployTaskEndpoint} -X 'POST' -d "{
-      \\"commit_sha\\":\\"the sha of deployment commit\\",
-      \\"repo_url\\":\\"the repo URL of the deployment commit\\",
-      \\"start_time\\":\\"Optional, eg. 2020-01-01T12:00:00+00:00\\"
-      }"`,
-      });
-    })();
-  }, [initialID]);
-
-  const handleUpdate = async () => {
-    const [success] = await operator(() => API.updateConnection(initialID, { name }), {
-      setOperating: setSaving,
-    });
-
-    if (success) {
-      onSubmitAfter?.(initialID);
-    }
-  };
-
-  return useMemo(
-    () => ({
-      saving,
-      name,
-      record,
-      onChangeName: setName,
-      onSubmit: handleUpdate,
-    }),
-    [saving, name, record],
-  );
-};