mirror of
https://github.com/logto-io/logto.git
synced 2025-03-31 22:51:25 -05:00
refactor(console): connector details page (#2465)
This commit is contained in:
parent
add9fb12c2
commit
146de08c9b
12 changed files with 75 additions and 57 deletions
|
@ -1,16 +1,17 @@
|
|||
import type { Connector, ConnectorResponse, ConnectorMetadata } from '@logto/schemas';
|
||||
import { ConnectorType } from '@logto/schemas';
|
||||
import { useMemo } from 'react';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import Button from '@/components/Button';
|
||||
import CodeEditor from '@/components/CodeEditor';
|
||||
import DetailsForm from '@/components/DetailsForm';
|
||||
import FormCard from '@/components/FormCard';
|
||||
import FormField from '@/components/FormField';
|
||||
import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import * as detailsStyles from '@/scss/details.module.scss';
|
||||
import { safeParseJson } from '@/utilities/json';
|
||||
|
||||
import * as styles from '../index.module.scss';
|
||||
|
@ -24,15 +25,15 @@ type Props = {
|
|||
|
||||
const ConnectorContent = ({ isDeleted, connectorData, onConnectorUpdated }: Props) => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { connectorId } = useParams();
|
||||
const api = useApi();
|
||||
const methods = useForm<{ configJson: string }>({ reValidateMode: 'onBlur' });
|
||||
const {
|
||||
control,
|
||||
formState: { isSubmitting, isDirty },
|
||||
handleSubmit,
|
||||
watch,
|
||||
reset,
|
||||
} = methods;
|
||||
} = useForm<{ configJson: string }>({ reValidateMode: 'onBlur' });
|
||||
|
||||
const defaultConfig = useMemo(() => {
|
||||
const hasData = Object.keys(connectorData.config).length > 0;
|
||||
|
@ -40,6 +41,10 @@ const ConnectorContent = ({ isDeleted, connectorData, onConnectorUpdated }: Prop
|
|||
return hasData ? JSON.stringify(connectorData.config, null, 2) : connectorData.configTemplate;
|
||||
}, [connectorData]);
|
||||
|
||||
useEffect(() => {
|
||||
reset();
|
||||
}, [connectorId, reset]);
|
||||
|
||||
const onSubmit = handleSubmit(async ({ configJson }) => {
|
||||
if (!configJson) {
|
||||
toast.error(t('connector_details.save_error_empty_config'));
|
||||
|
@ -66,8 +71,16 @@ const ConnectorContent = ({ isDeleted, connectorData, onConnectorUpdated }: Prop
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.main}>
|
||||
<form {...methods}>
|
||||
<DetailsForm
|
||||
isDirty={isDirty}
|
||||
isSubmitting={isSubmitting}
|
||||
onDiscard={reset}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<FormCard
|
||||
title="connector_details.settings"
|
||||
description="connector_details.settings_description"
|
||||
>
|
||||
<Controller
|
||||
name="configJson"
|
||||
control={control}
|
||||
|
@ -83,26 +96,16 @@ const ConnectorContent = ({ isDeleted, connectorData, onConnectorUpdated }: Prop
|
|||
</FormField>
|
||||
)}
|
||||
/>
|
||||
</form>
|
||||
{connectorData.type !== ConnectorType.Social && (
|
||||
<SenderTester
|
||||
connectorId={connectorData.id}
|
||||
connectorType={connectorData.type}
|
||||
config={watch('configJson')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className={detailsStyles.footer}>
|
||||
<div className={detailsStyles.footerMain}>
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
title="general.save_changes"
|
||||
isLoading={isSubmitting}
|
||||
onClick={onSubmit}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{connectorData.type !== ConnectorType.Social && (
|
||||
<SenderTester
|
||||
className={styles.senderTest}
|
||||
connectorId={connectorData.id}
|
||||
connectorType={connectorData.type}
|
||||
config={watch('configJson')}
|
||||
/>
|
||||
)}
|
||||
</FormCard>
|
||||
</DetailsForm>
|
||||
<UnsavedChangesAlertModal hasUnsavedChanges={!isDeleted && isDirty} />
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -57,6 +57,7 @@ const SenderTester = ({ connectorId, connectorType, config, className }: Props)
|
|||
|
||||
const onSubmit = handleSubmit(async (formData) => {
|
||||
const { sendTo } = formData;
|
||||
|
||||
const result = safeParseJson(config);
|
||||
|
||||
if (!result.success) {
|
||||
|
@ -73,7 +74,7 @@ const SenderTester = ({ connectorId, connectorType, config, className }: Props)
|
|||
});
|
||||
|
||||
return (
|
||||
<form className={className}>
|
||||
<div className={className}>
|
||||
<div className={styles.fields}>
|
||||
<FormField
|
||||
isRequired
|
||||
|
@ -120,7 +121,7 @@ const SenderTester = ({ connectorId, connectorType, config, className }: Props)
|
|||
<div className={classNames(inputError?.message ? styles.error : styles.description)}>
|
||||
{inputError?.message ?? t('connector_details.test_sender_description')}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -76,18 +76,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
> :not(:first-child) {
|
||||
margin-top: _.unit(4);
|
||||
}
|
||||
.codeEditor {
|
||||
margin-bottom: _.unit(6);
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1;
|
||||
|
||||
.codeEditor {
|
||||
margin-bottom: _.unit(6);
|
||||
}
|
||||
}
|
||||
.senderTest {
|
||||
margin-top: _.unit(6);
|
||||
}
|
||||
|
||||
.resetIcon {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import type { ConnectorResponse } from '@logto/schemas';
|
||||
import { AppearanceMode, ConnectorType } from '@logto/schemas';
|
||||
import classNames from 'classnames';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
|
@ -73,11 +72,12 @@ const ConnectorDetails = () => {
|
|||
json: { enabled: false },
|
||||
})
|
||||
.json<ConnectorResponse>();
|
||||
toast.success(t('connector_details.connector_deleted'));
|
||||
|
||||
await mutateGlobal('/api/connectors');
|
||||
setIsDeleted(true);
|
||||
|
||||
toast.success(t('connector_details.connector_deleted'));
|
||||
await mutateGlobal('/api/connectors');
|
||||
|
||||
if (isSocial) {
|
||||
navigate(`/connectors/social`, { replace: true });
|
||||
} else {
|
||||
|
@ -178,21 +178,19 @@ const ConnectorDetails = () => {
|
|||
</div>
|
||||
</Card>
|
||||
)}
|
||||
<TabNav>
|
||||
<TabNavItem href={`/connectors/${connectorId ?? ''}`}>
|
||||
{t('general.settings_nav')}
|
||||
</TabNavItem>
|
||||
</TabNav>
|
||||
{data && (
|
||||
<Card className={classNames(styles.body, detailsStyles.body)}>
|
||||
<TabNav>
|
||||
<TabNavItem href={`/connectors/${connectorId ?? ''}`}>
|
||||
{t('general.settings_nav')}
|
||||
</TabNavItem>
|
||||
</TabNav>
|
||||
<ConnectorContent
|
||||
isDeleted={isDeleted}
|
||||
connectorData={data}
|
||||
onConnectorUpdated={(connector) => {
|
||||
void mutate(connector);
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
<ConnectorContent
|
||||
isDeleted={isDeleted}
|
||||
connectorData={data}
|
||||
onConnectorUpdated={(connector) => {
|
||||
void mutate(connector);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{data && (
|
||||
<ConfirmModal
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
.container {
|
||||
width: 100%;
|
||||
padding-bottom: _.unit(6);
|
||||
@include _.flex-column;
|
||||
padding-bottom: _.unit(6);
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
const connector_details = {
|
||||
back_to_connectors: 'Zurück zu Connectoren',
|
||||
check_readme: 'Zur README',
|
||||
settings: 'Settings', // UNTRANSLATED
|
||||
settings_description:
|
||||
'It real sent your at. Amounted all shy set why followed declared. Repeated of endeavor mr position kindness offering ignorant so up. Simplicity are melancholy preference considered saw companions.', // UNTRANSLATED
|
||||
save_error_empty_config: 'Bitte fülle die Konfiguration aus',
|
||||
send: 'Senden',
|
||||
send_error_invalid_format: 'Ungültige Eingabe',
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
const connector_details = {
|
||||
back_to_connectors: 'Back to Connectors',
|
||||
check_readme: 'Check README',
|
||||
settings: 'Settings',
|
||||
settings_description:
|
||||
'It real sent your at. Amounted all shy set why followed declared. Repeated of endeavor mr position kindness offering ignorant so up. Simplicity are melancholy preference considered saw companions.', // UNTRANSLATED
|
||||
save_error_empty_config: 'Please enter config',
|
||||
send: 'Send',
|
||||
send_error_invalid_format: 'Invalid input',
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
const connector_details = {
|
||||
back_to_connectors: 'Retour à Connecteurs',
|
||||
check_readme: 'Vérifier le README',
|
||||
settings: 'Settings', // UNTRANSLATED
|
||||
settings_description:
|
||||
'It real sent your at. Amounted all shy set why followed declared. Repeated of endeavor mr position kindness offering ignorant so up. Simplicity are melancholy preference considered saw companions.', // UNTRANSLATED
|
||||
save_error_empty_config: 'Veuillez entrer la configuration',
|
||||
send: 'Envoyer',
|
||||
send_error_invalid_format: 'Entrée non valide',
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
const connector_details = {
|
||||
back_to_connectors: '연동으로 돌아가기',
|
||||
check_readme: 'README 확인',
|
||||
settings: 'Settings', // UNTRANSLATED
|
||||
settings_description:
|
||||
'It real sent your at. Amounted all shy set why followed declared. Repeated of endeavor mr position kindness offering ignorant so up. Simplicity are melancholy preference considered saw companions.', // UNTRANSLATED
|
||||
save_error_empty_config: '설정을 입력해주세요.',
|
||||
send: '보내기',
|
||||
send_error_invalid_format: '유효하지 않은 입력',
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
const connector_details = {
|
||||
back_to_connectors: 'Voltar para Conectores',
|
||||
check_readme: 'Verifique o README',
|
||||
settings: 'Settings', // UNTRANSLATED
|
||||
settings_description:
|
||||
'It real sent your at. Amounted all shy set why followed declared. Repeated of endeavor mr position kindness offering ignorant so up. Simplicity are melancholy preference considered saw companions.', // UNTRANSLATED
|
||||
save_error_empty_config: 'Por favor, insira a configuração',
|
||||
send: 'Enviar',
|
||||
send_error_invalid_format: 'Entrada inválida',
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
const connector_details = {
|
||||
back_to_connectors: 'Connectorlara dön',
|
||||
check_readme: 'READMEye göz at',
|
||||
settings: 'Settings', // UNTRANSLATED
|
||||
settings_description:
|
||||
'It real sent your at. Amounted all shy set why followed declared. Repeated of endeavor mr position kindness offering ignorant so up. Simplicity are melancholy preference considered saw companions.', // UNTRANSLATED
|
||||
save_error_empty_config: 'Lütfen yapılandırmayı girin',
|
||||
send: 'Gönder',
|
||||
send_error_invalid_format: 'Geçersiz input',
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
const connector_details = {
|
||||
back_to_connectors: '返回连接器',
|
||||
check_readme: '查看 README',
|
||||
settings: '设置',
|
||||
settings_description:
|
||||
'It real sent your at. Amounted all shy set why followed declared. Repeated of endeavor mr position kindness offering ignorant so up. Simplicity are melancholy preference considered saw companions.', // UNTRANSLATED
|
||||
save_error_empty_config: '请输入配置内容',
|
||||
send: '发送',
|
||||
send_error_invalid_format: '无效输入',
|
||||
|
|
Loading…
Add table
Reference in a new issue