mirror of
https://github.com/logto-io/logto.git
synced 2025-02-17 22:04:19 -05:00
fix(console): add missing loading state for confirm modals (#3610)
This commit is contained in:
parent
a33173d9c9
commit
5f1c9a579b
6 changed files with 76 additions and 43 deletions
|
@ -9,11 +9,19 @@ type Props = {
|
||||||
data: ConnectorResponse;
|
data: ConnectorResponse;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
isInUse: boolean;
|
isInUse: boolean;
|
||||||
|
isLoading: boolean;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
onConfirm: () => void;
|
onConfirm: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
function DeleteConnectorConfirmModal({ data, isOpen, isInUse, onCancel, onConfirm }: Props) {
|
function DeleteConnectorConfirmModal({
|
||||||
|
data,
|
||||||
|
isOpen,
|
||||||
|
isInUse,
|
||||||
|
isLoading,
|
||||||
|
onCancel,
|
||||||
|
onConfirm,
|
||||||
|
}: Props) {
|
||||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||||
const isSocial = data.type === ConnectorType.Social;
|
const isSocial = data.type === ConnectorType.Social;
|
||||||
|
|
||||||
|
@ -21,6 +29,7 @@ function DeleteConnectorConfirmModal({ data, isOpen, isInUse, onCancel, onConfir
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
confirmButtonText="general.delete"
|
confirmButtonText="general.delete"
|
||||||
|
isLoading={isLoading}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
onConfirm={onConfirm}
|
onConfirm={onConfirm}
|
||||||
>
|
>
|
||||||
|
|
|
@ -68,36 +68,32 @@ function ConnectorDetails() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const isSocial = data?.type === ConnectorType.Social;
|
const isSocial = data?.type === ConnectorType.Social;
|
||||||
const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState(false);
|
const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState(false);
|
||||||
|
const [isDeleting, setIsDeleting] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsDeleteAlertOpen(false);
|
setIsDeleteAlertOpen(false);
|
||||||
}, [pathname]);
|
}, [pathname]);
|
||||||
|
|
||||||
const onDeleteClick = async () => {
|
|
||||||
if (!inUse) {
|
|
||||||
await handleDelete();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsDeleteAlertOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDelete = async () => {
|
const handleDelete = async () => {
|
||||||
if (!connectorId) {
|
if (!connectorId || isDeleting) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setIsDeleting(true);
|
||||||
|
|
||||||
await api.delete(`api/connectors/${connectorId}`).json<ConnectorResponse>();
|
try {
|
||||||
|
await api.delete(`api/connectors/${connectorId}`).json<ConnectorResponse>();
|
||||||
|
|
||||||
setIsDeleted(true);
|
setIsDeleted(true);
|
||||||
|
|
||||||
toast.success(t('connector_details.connector_deleted'));
|
toast.success(t('connector_details.connector_deleted'));
|
||||||
await mutateGlobal('api/connectors');
|
await mutateGlobal('api/connectors');
|
||||||
|
|
||||||
navigate(getConnectorsPathname(isSocial), {
|
navigate(getConnectorsPathname(isSocial), {
|
||||||
replace: true,
|
replace: true,
|
||||||
});
|
});
|
||||||
|
} finally {
|
||||||
|
setIsDeleting(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!connectorId) {
|
if (!connectorId) {
|
||||||
|
@ -225,6 +221,7 @@ function ConnectorDetails() {
|
||||||
data={data}
|
data={data}
|
||||||
isInUse={inUse}
|
isInUse={inUse}
|
||||||
isOpen={isDeleteAlertOpen}
|
isOpen={isDeleteAlertOpen}
|
||||||
|
isLoading={isDeleting}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
setIsDeleteAlertOpen(false);
|
setIsDeleteAlertOpen(false);
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -26,24 +26,30 @@ function ConnectorDeleteButton({ connectorGroup }: Props) {
|
||||||
const inUse = isConnectorInUse(firstConnector);
|
const inUse = isConnectorInUse(firstConnector);
|
||||||
|
|
||||||
const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState(false);
|
const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState(false);
|
||||||
|
const [isDeleting, setIsDeleting] = useState(false);
|
||||||
|
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
|
|
||||||
const handleDelete = async () => {
|
const handleDelete = async () => {
|
||||||
if (!firstConnector) {
|
if (!firstConnector || isDeleting) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setIsDeleting(true);
|
||||||
|
|
||||||
const { connectors } = connectorGroup;
|
const { connectors } = connectorGroup;
|
||||||
|
|
||||||
await Promise.all(
|
try {
|
||||||
connectors.map(async (connector) => {
|
await Promise.all(
|
||||||
await api.delete(`api/connectors/${connector.id}`).json<ConnectorResponse>();
|
connectors.map(async (connector) => {
|
||||||
})
|
await api.delete(`api/connectors/${connector.id}`).json<ConnectorResponse>();
|
||||||
);
|
})
|
||||||
|
);
|
||||||
|
|
||||||
toast.success(t('connector_details.connector_deleted'));
|
toast.success(t('connector_details.connector_deleted'));
|
||||||
await mutateGlobal('api/connectors');
|
await mutateGlobal('api/connectors');
|
||||||
|
} finally {
|
||||||
|
setIsDeleting(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!firstConnector) {
|
if (!firstConnector) {
|
||||||
|
@ -65,6 +71,7 @@ function ConnectorDeleteButton({ connectorGroup }: Props) {
|
||||||
data={firstConnector}
|
data={firstConnector}
|
||||||
isInUse={inUse}
|
isInUse={inUse}
|
||||||
isOpen={isDeleteAlertOpen}
|
isOpen={isDeleteAlertOpen}
|
||||||
|
isLoading={isDeleting}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
setIsDeleteAlertOpen(false);
|
setIsDeleteAlertOpen(false);
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -20,11 +20,21 @@ function GetStarted() {
|
||||||
const { data, isLoading } = useGetStartedMetadata();
|
const { data, isLoading } = useGetStartedMetadata();
|
||||||
const { update } = useUserPreferences();
|
const { update } = useUserPreferences();
|
||||||
const [showConfirmModal, setShowConfirmModal] = useState(false);
|
const [showConfirmModal, setShowConfirmModal] = useState(false);
|
||||||
|
const [isUpdating, setIsUpdating] = useState(false);
|
||||||
|
|
||||||
const hideGetStarted = () => {
|
const hideGetStarted = async () => {
|
||||||
void update({ getStartedHidden: true });
|
if (isUpdating) {
|
||||||
// Navigate to next menu item
|
return;
|
||||||
navigate('/dashboard');
|
}
|
||||||
|
setIsUpdating(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await update({ getStartedHidden: true });
|
||||||
|
// Navigate to next menu item
|
||||||
|
navigate('/dashboard');
|
||||||
|
} finally {
|
||||||
|
setIsUpdating(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const showConfirmModalHandler = () => {
|
const showConfirmModalHandler = () => {
|
||||||
|
@ -77,6 +87,7 @@ function GetStarted() {
|
||||||
isOpen={showConfirmModal}
|
isOpen={showConfirmModal}
|
||||||
confirmButtonType="primary"
|
confirmButtonType="primary"
|
||||||
confirmButtonText="get_started.hide_this"
|
confirmButtonText="get_started.hide_this"
|
||||||
|
isLoading={isUpdating}
|
||||||
onConfirm={hideGetStarted}
|
onConfirm={hideGetStarted}
|
||||||
onCancel={hideConfirmModalHandler}
|
onCancel={hideConfirmModalHandler}
|
||||||
>
|
>
|
||||||
|
|
|
@ -65,6 +65,7 @@ function SignInExperience() {
|
||||||
const { data, error, mutate } = useSWR<SignInExperienceType, RequestError>('api/sign-in-exp');
|
const { data, error, mutate } = useSWR<SignInExperienceType, RequestError>('api/sign-in-exp');
|
||||||
const isLoadingSignInExperience = !data && !error;
|
const isLoadingSignInExperience = !data && !error;
|
||||||
const { isLoading: isUserAssetsServiceLoading } = useUserAssetsService();
|
const { isLoading: isUserAssetsServiceLoading } = useUserAssetsService();
|
||||||
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
configs,
|
configs,
|
||||||
|
@ -93,7 +94,7 @@ function SignInExperience() {
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
getValues,
|
getValues,
|
||||||
watch,
|
watch,
|
||||||
formState: { isSubmitting, isDirty, errors },
|
formState: { isDirty, errors },
|
||||||
} = methods;
|
} = methods;
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
const formData = watch();
|
const formData = watch();
|
||||||
|
@ -115,19 +116,25 @@ function SignInExperience() {
|
||||||
}, [reset, defaultFormData]);
|
}, [reset, defaultFormData]);
|
||||||
|
|
||||||
const saveData = async () => {
|
const saveData = async () => {
|
||||||
const updatedData = await api
|
setIsSaving(true);
|
||||||
.patch('api/sign-in-exp', {
|
|
||||||
json: signInExperienceParser.toRemoteModel(getValues()),
|
try {
|
||||||
})
|
const updatedData = await api
|
||||||
.json<SignInExperienceType>();
|
.patch('api/sign-in-exp', {
|
||||||
void mutate(updatedData);
|
json: signInExperienceParser.toRemoteModel(getValues()),
|
||||||
setDataToCompare(undefined);
|
})
|
||||||
await updateConfigs({ signInExperienceCustomized: true });
|
.json<SignInExperienceType>();
|
||||||
toast.success(t('general.saved'));
|
void mutate(updatedData);
|
||||||
|
setDataToCompare(undefined);
|
||||||
|
await updateConfigs({ signInExperienceCustomized: true });
|
||||||
|
toast.success(t('general.saved'));
|
||||||
|
} finally {
|
||||||
|
setIsSaving(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSubmit = handleSubmit(async (formData: SignInExperienceForm) => {
|
const onSubmit = handleSubmit(async (formData: SignInExperienceForm) => {
|
||||||
if (!data || isSubmitting) {
|
if (!data || isSaving) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +224,7 @@ function SignInExperience() {
|
||||||
</div>
|
</div>
|
||||||
<SubmitFormChangesActionBar
|
<SubmitFormChangesActionBar
|
||||||
isOpen={isDirty}
|
isOpen={isDirty}
|
||||||
isSubmitting={isSubmitting}
|
isSubmitting={isSaving}
|
||||||
onDiscard={reset}
|
onDiscard={reset}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
/>
|
/>
|
||||||
|
@ -226,6 +233,7 @@ function SignInExperience() {
|
||||||
{data && (
|
{data && (
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
isOpen={Boolean(dataToCompare)}
|
isOpen={Boolean(dataToCompare)}
|
||||||
|
isLoading={isSaving}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
setDataToCompare(undefined);
|
setDataToCompare(undefined);
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -136,6 +136,7 @@ function UserSocialIdentities({ userId, identities, onDelete }: Props) {
|
||||||
)}
|
)}
|
||||||
<DeleteConfirmModal
|
<DeleteConfirmModal
|
||||||
isOpen={deletingConnector !== undefined}
|
isOpen={deletingConnector !== undefined}
|
||||||
|
isLoading={isSubmitting}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
setDeletingConnector(undefined);
|
setDeletingConnector(undefined);
|
||||||
}}
|
}}
|
||||||
|
|
Loading…
Add table
Reference in a new issue