From 78b2e3be30255c6e1087931acd3e8283d1d08632 Mon Sep 17 00:00:00 2001 From: Xiao Yijun Date: Wed, 16 Mar 2022 14:47:31 +0800 Subject: [PATCH] feat(console): delete api resource (#389) --- .../components/ActionMenu/ActionMenuItem.tsx | 12 ++- .../src/components/ActionMenu/index.tsx | 9 +- .../components/DeleteForm/index.module.scss | 41 +++++++++ .../components/DeleteForm/index.tsx | 86 +++++++++++++++++++ .../src/pages/ApiResourceDetails/index.tsx | 31 +++++++ packages/phrases/src/locales/en.ts | 10 +++ packages/phrases/src/locales/zh-cn.ts | 10 +++ 7 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 packages/console/src/pages/ApiResourceDetails/components/DeleteForm/index.module.scss create mode 100644 packages/console/src/pages/ApiResourceDetails/components/DeleteForm/index.tsx diff --git a/packages/console/src/components/ActionMenu/ActionMenuItem.tsx b/packages/console/src/components/ActionMenu/ActionMenuItem.tsx index f7f838ce2..2bc18cd79 100644 --- a/packages/console/src/components/ActionMenu/ActionMenuItem.tsx +++ b/packages/console/src/components/ActionMenu/ActionMenuItem.tsx @@ -3,9 +3,19 @@ import React from 'react'; import * as styles from './ActionMenuItem.module.scss'; type Props = { + onClick?: () => void; children: React.ReactNode; }; -const ActionMenuItem = ({ children }: Props) =>
  • {children}
  • ; +const ActionMenuItem = ({ onClick, children }: Props) => ( +
  • { + onClick?.(); + }} + > + {children} +
  • +); export default ActionMenuItem; diff --git a/packages/console/src/components/ActionMenu/index.tsx b/packages/console/src/components/ActionMenu/index.tsx index 49b77c6a8..6dd330584 100644 --- a/packages/console/src/components/ActionMenu/index.tsx +++ b/packages/console/src/components/ActionMenu/index.tsx @@ -47,7 +47,14 @@ const ActionMenu = ({ children, buttonProps, title }: Props) => { >
    {title &&
    {title}
    } - +
    diff --git a/packages/console/src/pages/ApiResourceDetails/components/DeleteForm/index.module.scss b/packages/console/src/pages/ApiResourceDetails/components/DeleteForm/index.module.scss new file mode 100644 index 000000000..0b06732e8 --- /dev/null +++ b/packages/console/src/pages/ApiResourceDetails/components/DeleteForm/index.module.scss @@ -0,0 +1,41 @@ +@use '@/scss/underscore' as _; + +.card { + padding: _.unit(8); + max-width: 800px; + + > :not(:first-child) { + margin-top: _.unit(6); + } + + .headline { + display: flex; + align-items: center; + justify-content: space-between; + + .close { + cursor: pointer; + } + } + + .line { + border-top: 1px solid var(--color-neutral-90); + } + + .description { + font: var(--font-body-2); + } + + .hightlight { + color: var(--color-primary-50); + } + + .actions { + text-align: right; + + > :not(:first-child) { + margin-left: _.unit(4); + } + } +} + diff --git a/packages/console/src/pages/ApiResourceDetails/components/DeleteForm/index.tsx b/packages/console/src/pages/ApiResourceDetails/components/DeleteForm/index.tsx new file mode 100644 index 000000000..838b21e50 --- /dev/null +++ b/packages/console/src/pages/ApiResourceDetails/components/DeleteForm/index.tsx @@ -0,0 +1,86 @@ +import React, { useState } from 'react'; +import { toast } from 'react-hot-toast'; +import { Trans, useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; + +import Button from '@/components/Button'; +import Card from '@/components/Card'; +import CardTitle from '@/components/CardTitle'; +import TextInput from '@/components/TextInput'; +import Close from '@/icons/Close'; +import api from '@/utilities/api'; + +import * as styles from './index.module.scss'; + +type Props = { + id: string; + name: string; + onClose: () => void; +}; + +const DeleteForm = ({ id, name, onClose }: Props) => { + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + + const navigate = useNavigate(); + + const [inputName, setInputName] = useState(''); + const [loading, setLoading] = useState(false); + + const inputMismatched = inputName !== name; + + const handleDelete = async () => { + setLoading(true); + + try { + await api.delete(`/api/resources/${id}`); + onClose(); + navigate(`/api-resources`); + toast.success(t('api_resource_details.api_resource_deleted', { name })); + } finally { + setLoading(false); + } + }; + + return ( + // TODO LOG-1907: Modal + // TODO LOG-1890: Icon Button + +
    + + +
    +
    + }} + /> +
    + { + setInputName(event.currentTarget.value); + }} + /> +
    +
    +
    + + ); +}; + +export default DeleteForm; diff --git a/packages/console/src/pages/ApiResourceDetails/index.tsx b/packages/console/src/pages/ApiResourceDetails/index.tsx index 4378f1f81..6bbb20cf6 100644 --- a/packages/console/src/pages/ApiResourceDetails/index.tsx +++ b/packages/console/src/pages/ApiResourceDetails/index.tsx @@ -2,9 +2,11 @@ import { Resource } from '@logto/schemas'; import React, { useEffect, useState } from 'react'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; +import Modal from 'react-modal'; import { useLocation, useParams } from 'react-router-dom'; import useSWR from 'swr'; +import ActionMenu, { ActionMenuItem } from '@/components/ActionMenu'; import BackLink from '@/components/BackLink'; import Button from '@/components/Button'; import Card from '@/components/Card'; @@ -13,9 +15,11 @@ import FormField from '@/components/FormField'; import ImagePlaceholder from '@/components/ImagePlaceholder'; import TabNav, { TabNavLink } from '@/components/TabNav'; import TextInput from '@/components/TextInput'; +import * as modalStyles from '@/scss/modal.module.scss'; import { RequestError } from '@/swr'; import api from '@/utilities/api'; +import DeleteForm from './components/DeleteForm'; import * as styles from './index.module.scss'; type FormData = { @@ -36,6 +40,8 @@ const ApiResourceDetails = () => { }); const [submitting, setSubmitting] = useState(false); + const [isDeleteFormOpen, setIsDeleteFormOpen] = useState(false); + useEffect(() => { if (!data) { return; @@ -79,6 +85,31 @@ const ApiResourceDetails = () => {
    diff --git a/packages/phrases/src/locales/en.ts b/packages/phrases/src/locales/en.ts index 4d3bb86a1..38df07870 100644 --- a/packages/phrases/src/locales/en.ts +++ b/packages/phrases/src/locales/en.ts @@ -104,9 +104,19 @@ const translation = { api_resource_details: { back_to_api_resources: 'Back to my API resources', check_help_guide: 'Check Help Guide', + options: 'Options', + more_options: 'More Options', + options_delete: 'Delete', settings: 'Settings', save_changes: 'Save Changes', token_expiration_time_in_seconds: 'Token Expiration Time (in seconds)', + reminder: 'Reminder', + delete_description: + 'This action cannot be undone. This will permanently delete the this application. Please enter the api resource name {{name}} to proceed.', + enter_your_api_resource_name: 'Enter your API resource name', + cancel: 'Cancel', + delete: 'Delete', + api_resource_deleted: 'The API Resource {{name}} deleted.', }, connectors: { title: 'Connectors', diff --git a/packages/phrases/src/locales/zh-cn.ts b/packages/phrases/src/locales/zh-cn.ts index 1fd309fd9..02b59c86d 100644 --- a/packages/phrases/src/locales/zh-cn.ts +++ b/packages/phrases/src/locales/zh-cn.ts @@ -106,9 +106,19 @@ const translation = { api_resource_details: { back_to_api_resources: 'Back to my API resources', check_help_guide: 'Check Help Guide', + options: 'Options', + more_options: 'More Options', + options_delete: 'Delete', settings: 'Settings', save_changes: 'Save Changes', token_expiration_time_in_seconds: 'Token Expiration Time (in seconds)', + reminder: 'Reminder', + delete_description: + 'This action cannot be undone. This will permanently delete the this application. Please enter the api resource name {{name}} to proceed.', + enter_your_api_resource_name: 'Enter your API resource name', + cancel: 'Cancel', + delete: 'Delete', + api_resource_deleted: 'The API Resource {{name}} deleted.', }, connectors: { title: '连接器',