Merge pull request #7108 from nixocio/ui_issue_7105

Make consistent usage of `useRequest` to delete items

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
softwarefactory-project-zuul[bot]
2020-05-21 12:55:34 +00:00
committed by GitHub
6 changed files with 100 additions and 115 deletions

View File

@@ -1,5 +1,5 @@
import 'styled-components/macro'; import 'styled-components/macro';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect } from 'react';
import { Link, useHistory, useLocation } from 'react-router-dom'; import { Link, useHistory, useLocation } from 'react-router-dom';
import { RRule, rrulestr } from 'rrule'; import { RRule, rrulestr } from 'rrule';
import styled from 'styled-components'; import styled from 'styled-components';
@@ -16,7 +16,7 @@ import { DetailList, Detail, UserDateDetail } from '../../DetailList';
import ScheduleOccurrences from '../ScheduleOccurrences'; import ScheduleOccurrences from '../ScheduleOccurrences';
import ScheduleToggle from '../ScheduleToggle'; import ScheduleToggle from '../ScheduleToggle';
import { formatDateString } from '../../../util/dates'; import { formatDateString } from '../../../util/dates';
import useRequest from '../../../util/useRequest'; import useRequest, { useDismissableError } from '../../../util/useRequest';
import { SchedulesAPI } from '../../../api'; import { SchedulesAPI } from '../../../api';
import DeleteButton from '../../DeleteButton'; import DeleteButton from '../../DeleteButton';
import ErrorDetail from '../../ErrorDetail'; import ErrorDetail from '../../ErrorDetail';
@@ -48,27 +48,27 @@ function ScheduleDetail({ schedule, i18n }) {
timezone, timezone,
} = schedule; } = schedule;
const [deletionError, setDeletionError] = useState(null);
const [hasContentLoading, setHasContentLoading] = useState(false);
const history = useHistory(); const history = useHistory();
const { pathname } = useLocation(); const { pathname } = useLocation();
const pathRoot = pathname.substr(0, pathname.indexOf('schedules')); const pathRoot = pathname.substr(0, pathname.indexOf('schedules'));
const handleDelete = async () => { const {
setHasContentLoading(true); request: deleteSchedule,
try { isLoading: isDeleteLoading,
error: deleteError,
} = useRequest(
useCallback(async () => {
await SchedulesAPI.destroy(id); await SchedulesAPI.destroy(id);
history.push(`${pathRoot}schedules`); history.push(`${pathRoot}schedules`);
} catch (error) { }, [id, history, pathRoot])
setDeletionError(error); );
}
setHasContentLoading(false); const { error, dismissError } = useDismissableError(deleteError);
};
const { const {
result: [credentials, preview], result: [credentials, preview],
isLoading, isLoading,
error, error: readContentError,
request: fetchCredentialsAndPreview, request: fetchCredentialsAndPreview,
} = useRequest( } = useRequest(
useCallback(async () => { useCallback(async () => {
@@ -102,12 +102,12 @@ function ScheduleDetail({ schedule, i18n }) {
(job_tags && job_tags.length > 0) || (job_tags && job_tags.length > 0) ||
(skip_tags && skip_tags.length > 0); (skip_tags && skip_tags.length > 0);
if (isLoading || hasContentLoading) { if (isLoading) {
return <ContentLoading />; return <ContentLoading />;
} }
if (error) { if (readContentError) {
return <ContentError error={error} />; return <ContentError error={readContentError} />;
} }
return ( return (
@@ -237,21 +237,22 @@ function ScheduleDetail({ schedule, i18n }) {
<DeleteButton <DeleteButton
name={name} name={name}
modalTitle={i18n._(t`Delete Schedule`)} modalTitle={i18n._(t`Delete Schedule`)}
onConfirm={handleDelete} onConfirm={deleteSchedule}
isDisabled={isDeleteLoading}
> >
{i18n._(t`Delete`)} {i18n._(t`Delete`)}
</DeleteButton> </DeleteButton>
)} )}
</CardActionsRow> </CardActionsRow>
{deletionError && ( {error && (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={error}
variant="danger" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={() => setDeletionError(null)} onClose={dismissError}
> >
{i18n._(t`Failed to delete schedule.`)} {i18n._(t`Failed to delete schedule.`)}
<ErrorDetail error={deletionError} /> <ErrorDetail error={error} />
</AlertModal> </AlertModal>
)} )}
</CardBody> </CardBody>

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect, useCallback } from 'react';
import { Link, useHistory } from 'react-router-dom'; import { Link, useHistory } from 'react-router-dom';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
@@ -18,6 +18,7 @@ import {
import ErrorDetail from '../../../components/ErrorDetail'; import ErrorDetail from '../../../components/ErrorDetail';
import { CredentialsAPI, CredentialTypesAPI } from '../../../api'; import { CredentialsAPI, CredentialTypesAPI } from '../../../api';
import { Credential } from '../../../types'; import { Credential } from '../../../types';
import useRequest, { useDismissableError } from '../../../util/useRequest';
function CredentialDetail({ i18n, credential }) { function CredentialDetail({ i18n, credential }) {
const { const {
@@ -39,7 +40,6 @@ function CredentialDetail({ i18n, credential }) {
const [fields, setFields] = useState([]); const [fields, setFields] = useState([]);
const [managedByTower, setManagedByTower] = useState([]); const [managedByTower, setManagedByTower] = useState([]);
const [contentError, setContentError] = useState(null); const [contentError, setContentError] = useState(null);
const [deletionError, setDeletionError] = useState(null);
const [hasContentLoading, setHasContentLoading] = useState(true); const [hasContentLoading, setHasContentLoading] = useState(true);
const history = useHistory(); const history = useHistory();
@@ -62,17 +62,18 @@ function CredentialDetail({ i18n, credential }) {
})(); })();
}, [credential_type]); }, [credential_type]);
const handleDelete = async () => { const {
setHasContentLoading(true); request: deleteCredential,
isLoading,
try { error: deleteError,
} = useRequest(
useCallback(async () => {
await CredentialsAPI.destroy(credentialId); await CredentialsAPI.destroy(credentialId);
history.push('/credentials'); history.push('/credentials');
} catch (error) { }, [credentialId, history])
setDeletionError(error); );
}
setHasContentLoading(false); const { error, dismissError } = useDismissableError(deleteError);
};
const renderDetail = ({ id, label, type }) => { const renderDetail = ({ id, label, type }) => {
let detail; let detail;
@@ -161,21 +162,22 @@ function CredentialDetail({ i18n, credential }) {
<DeleteButton <DeleteButton
name={name} name={name}
modalTitle={i18n._(t`Delete Credential`)} modalTitle={i18n._(t`Delete Credential`)}
onConfirm={handleDelete} onConfirm={deleteCredential}
isLoading={isLoading}
> >
{i18n._(t`Delete`)} {i18n._(t`Delete`)}
</DeleteButton> </DeleteButton>
)} )}
</CardActionsRow> </CardActionsRow>
{deletionError && ( {error && (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={error}
variant="error" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={() => setDeletionError(null)} onClose={dismissError}
> >
{i18n._(t`Failed to delete credential.`)} {i18n._(t`Failed to delete credential.`)}
<ErrorDetail error={deletionError} /> <ErrorDetail error={error} />
</AlertModal> </AlertModal>
)} )}
</CardBody> </CardBody>

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState, useCallback } from 'react';
import { Link, useHistory, useRouteMatch } from 'react-router-dom'; import { Link, useHistory, useRouteMatch } from 'react-router-dom';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
@@ -16,6 +16,7 @@ import ContentError from '../../../components/ContentError';
import ContentLoading from '../../../components/ContentLoading'; import ContentLoading from '../../../components/ContentLoading';
import DeleteButton from '../../../components/DeleteButton'; import DeleteButton from '../../../components/DeleteButton';
import ErrorDetail from '../../../components/ErrorDetail'; import ErrorDetail from '../../../components/ErrorDetail';
import useRequest, { useDismissableError } from '../../../util/useRequest';
function OrganizationDetail({ i18n, organization }) { function OrganizationDetail({ i18n, organization }) {
const { const {
@@ -31,7 +32,6 @@ function OrganizationDetail({ i18n, organization }) {
summary_fields, summary_fields,
} = organization; } = organization;
const [contentError, setContentError] = useState(null); const [contentError, setContentError] = useState(null);
const [deletionError, setDeletionError] = useState(null);
const [hasContentLoading, setHasContentLoading] = useState(true); const [hasContentLoading, setHasContentLoading] = useState(true);
const [instanceGroups, setInstanceGroups] = useState([]); const [instanceGroups, setInstanceGroups] = useState([]);
const history = useHistory(); const history = useHistory();
@@ -53,16 +53,18 @@ function OrganizationDetail({ i18n, organization }) {
})(); })();
}, [id]); }, [id]);
const handleDelete = async () => { const {
setHasContentLoading(true); request: deleteOrganization,
try { isLoading,
error: deleteError,
} = useRequest(
useCallback(async () => {
await OrganizationsAPI.destroy(id); await OrganizationsAPI.destroy(id);
history.push(`/organizations`); history.push(`/organizations`);
} catch (error) { }, [id, history])
setDeletionError(error); );
}
setHasContentLoading(false); const { error, dismissError } = useDismissableError(deleteError);
};
if (hasContentLoading) { if (hasContentLoading) {
return <ContentLoading />; return <ContentLoading />;
@@ -127,22 +129,23 @@ function OrganizationDetail({ i18n, organization }) {
<DeleteButton <DeleteButton
name={name} name={name}
modalTitle={i18n._(t`Delete Organization`)} modalTitle={i18n._(t`Delete Organization`)}
onConfirm={handleDelete} onConfirm={deleteOrganization}
isDisabled={isLoading}
> >
{i18n._(t`Delete`)} {i18n._(t`Delete`)}
</DeleteButton> </DeleteButton>
)} )}
</CardActionsRow> </CardActionsRow>
{/* Update delete modal to show dependencies https://github.com/ansible/awx/issues/5546 */} {/* Update delete modal to show dependencies https://github.com/ansible/awx/issues/5546 */}
{deletionError && ( {error && (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={error}
variant="error" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={() => setDeletionError(null)} onClose={dismissError}
> >
{i18n._(t`Failed to delete organization.`)} {i18n._(t`Failed to delete organization.`)}
<ErrorDetail error={deletionError} /> <ErrorDetail error={error} />
</AlertModal> </AlertModal>
)} )}
</CardBody> </CardBody>

View File

@@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React, { useCallback } from 'react';
import { Link, useHistory } from 'react-router-dom'; import { Link, useHistory } from 'react-router-dom';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
@@ -8,7 +8,6 @@ import { Config } from '../../../contexts/Config';
import AlertModal from '../../../components/AlertModal'; import AlertModal from '../../../components/AlertModal';
import { CardBody, CardActionsRow } from '../../../components/Card'; import { CardBody, CardActionsRow } from '../../../components/Card';
import ContentLoading from '../../../components/ContentLoading';
import DeleteButton from '../../../components/DeleteButton'; import DeleteButton from '../../../components/DeleteButton';
import { import {
DetailList, DetailList,
@@ -19,6 +18,7 @@ import ErrorDetail from '../../../components/ErrorDetail';
import CredentialChip from '../../../components/CredentialChip'; import CredentialChip from '../../../components/CredentialChip';
import { ProjectsAPI } from '../../../api'; import { ProjectsAPI } from '../../../api';
import { toTitleCase } from '../../../util/strings'; import { toTitleCase } from '../../../util/strings';
import useRequest, { useDismissableError } from '../../../util/useRequest';
function ProjectDetail({ project, i18n }) { function ProjectDetail({ project, i18n }) {
const { const {
@@ -40,20 +40,16 @@ function ProjectDetail({ project, i18n }) {
scm_url, scm_url,
summary_fields, summary_fields,
} = project; } = project;
const [deletionError, setDeletionError] = useState(null);
const [hasContentLoading, setHasContentLoading] = useState(false);
const history = useHistory(); const history = useHistory();
const handleDelete = async () => { const { request: deleteProject, isLoading, error: deleteError } = useRequest(
setHasContentLoading(true); useCallback(async () => {
try {
await ProjectsAPI.destroy(id); await ProjectsAPI.destroy(id);
history.push(`/projects`); history.push(`/projects`);
} catch (error) { }, [id, history])
setDeletionError(error); );
}
setHasContentLoading(false); const { error, dismissError } = useDismissableError(deleteError);
};
let optionsList = ''; let optionsList = '';
if ( if (
@@ -78,10 +74,6 @@ function ProjectDetail({ project, i18n }) {
); );
} }
if (hasContentLoading) {
return <ContentLoading />;
}
return ( return (
<CardBody> <CardBody>
<DetailList gutter="sm"> <DetailList gutter="sm">
@@ -171,22 +163,23 @@ function ProjectDetail({ project, i18n }) {
<DeleteButton <DeleteButton
name={name} name={name}
modalTitle={i18n._(t`Delete Project`)} modalTitle={i18n._(t`Delete Project`)}
onConfirm={handleDelete} onConfirm={deleteProject}
isDisabled={isLoading}
> >
{i18n._(t`Delete`)} {i18n._(t`Delete`)}
</DeleteButton> </DeleteButton>
)} )}
</CardActionsRow> </CardActionsRow>
{/* Update delete modal to show dependencies https://github.com/ansible/awx/issues/5546 */} {/* Update delete modal to show dependencies https://github.com/ansible/awx/issues/5546 */}
{deletionError && ( {error && (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={error}
variant="error" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={() => setDeletionError(null)} onClose={dismissError}
> >
{i18n._(t`Failed to delete project.`)} {i18n._(t`Failed to delete project.`)}
<ErrorDetail error={deletionError} /> <ErrorDetail error={error} />
</AlertModal> </AlertModal>
)} )}
</CardBody> </CardBody>

View File

@@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React, { useCallback } from 'react';
import { Link, useHistory, useParams } from 'react-router-dom'; import { Link, useHistory, useParams } from 'react-router-dom';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
@@ -6,34 +6,26 @@ import { Button } from '@patternfly/react-core';
import AlertModal from '../../../components/AlertModal'; import AlertModal from '../../../components/AlertModal';
import { CardBody, CardActionsRow } from '../../../components/Card'; import { CardBody, CardActionsRow } from '../../../components/Card';
import ContentLoading from '../../../components/ContentLoading';
import DeleteButton from '../../../components/DeleteButton'; import DeleteButton from '../../../components/DeleteButton';
import { DetailList, Detail } from '../../../components/DetailList'; import { DetailList, Detail } from '../../../components/DetailList';
import ErrorDetail from '../../../components/ErrorDetail'; import ErrorDetail from '../../../components/ErrorDetail';
import { formatDateString } from '../../../util/dates'; import { formatDateString } from '../../../util/dates';
import { TeamsAPI } from '../../../api'; import { TeamsAPI } from '../../../api';
import useRequest, { useDismissableError } from '../../../util/useRequest';
function TeamDetail({ team, i18n }) { function TeamDetail({ team, i18n }) {
const { name, description, created, modified, summary_fields } = team; const { name, description, created, modified, summary_fields } = team;
const [deletionError, setDeletionError] = useState(null);
const [hasContentLoading, setHasContentLoading] = useState(false);
const history = useHistory(); const history = useHistory();
const { id } = useParams(); const { id } = useParams();
const handleDelete = async () => { const { request: deleteTeam, isLoading, error: deleteError } = useRequest(
setHasContentLoading(true); useCallback(async () => {
try {
await TeamsAPI.destroy(id); await TeamsAPI.destroy(id);
history.push(`/teams`); history.push(`/teams`);
} catch (error) { }, [id, history])
setDeletionError(error); );
}
setHasContentLoading(false);
};
if (hasContentLoading) { const { error, dismissError } = useDismissableError(deleteError);
return <ContentLoading />;
}
return ( return (
<CardBody> <CardBody>
@@ -74,21 +66,22 @@ function TeamDetail({ team, i18n }) {
<DeleteButton <DeleteButton
name={name} name={name}
modalTitle={i18n._(t`Delete Team`)} modalTitle={i18n._(t`Delete Team`)}
onConfirm={handleDelete} onConfirm={deleteTeam}
isDisabled={isLoading}
> >
{i18n._(t`Delete`)} {i18n._(t`Delete`)}
</DeleteButton> </DeleteButton>
)} )}
</CardActionsRow> </CardActionsRow>
{deletionError && ( {error && (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={error}
variant="error" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={() => setDeletionError(null)} onClose={dismissError}
> >
{i18n._(t`Failed to delete team.`)} {i18n._(t`Failed to delete team.`)}
<ErrorDetail error={deletionError} /> <ErrorDetail error={error} />
</AlertModal> </AlertModal>
)} )}
</CardBody> </CardBody>

View File

@@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React, { useCallback } from 'react';
import { Link, useHistory } from 'react-router-dom'; import { Link, useHistory } from 'react-router-dom';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
@@ -6,12 +6,12 @@ import { t } from '@lingui/macro';
import { Button } from '@patternfly/react-core'; import { Button } from '@patternfly/react-core';
import AlertModal from '../../../components/AlertModal'; import AlertModal from '../../../components/AlertModal';
import { CardBody, CardActionsRow } from '../../../components/Card'; import { CardBody, CardActionsRow } from '../../../components/Card';
import ContentLoading from '../../../components/ContentLoading';
import DeleteButton from '../../../components/DeleteButton'; import DeleteButton from '../../../components/DeleteButton';
import { DetailList, Detail } from '../../../components/DetailList'; import { DetailList, Detail } from '../../../components/DetailList';
import ErrorDetail from '../../../components/ErrorDetail'; import ErrorDetail from '../../../components/ErrorDetail';
import { formatDateString } from '../../../util/dates'; import { formatDateString } from '../../../util/dates';
import { UsersAPI } from '../../../api'; import { UsersAPI } from '../../../api';
import useRequest, { useDismissableError } from '../../../util/useRequest';
function UserDetail({ user, i18n }) { function UserDetail({ user, i18n }) {
const { const {
@@ -26,20 +26,16 @@ function UserDetail({ user, i18n }) {
is_system_auditor, is_system_auditor,
summary_fields, summary_fields,
} = user; } = user;
const [deletionError, setDeletionError] = useState(null);
const [hasContentLoading, setHasContentLoading] = useState(false);
const history = useHistory(); const history = useHistory();
const handleDelete = async () => { const { request: deleteUser, isLoading, error: deleteError } = useRequest(
setHasContentLoading(true); useCallback(async () => {
try {
await UsersAPI.destroy(id); await UsersAPI.destroy(id);
history.push(`/users`); history.push(`/users`);
} catch (error) { }, [id, history])
setDeletionError(error); );
}
setHasContentLoading(false); const { error, dismissError } = useDismissableError(deleteError);
};
let user_type; let user_type;
if (is_superuser) { if (is_superuser) {
@@ -50,10 +46,6 @@ function UserDetail({ user, i18n }) {
user_type = i18n._(t`Normal User`); user_type = i18n._(t`Normal User`);
} }
if (hasContentLoading) {
return <ContentLoading />;
}
return ( return (
<CardBody> <CardBody>
<DetailList> <DetailList>
@@ -90,21 +82,22 @@ function UserDetail({ user, i18n }) {
<DeleteButton <DeleteButton
name={username} name={username}
modalTitle={i18n._(t`Delete User`)} modalTitle={i18n._(t`Delete User`)}
onConfirm={handleDelete} onConfirm={deleteUser}
isDisabled={isLoading}
> >
{i18n._(t`Delete`)} {i18n._(t`Delete`)}
</DeleteButton> </DeleteButton>
)} )}
</CardActionsRow> </CardActionsRow>
{deletionError && ( {error && (
<AlertModal <AlertModal
isOpen={deletionError} isOpen={error}
variant="error" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
onClose={() => setDeletionError(null)} onClose={dismissError}
> >
{i18n._(t`Failed to delete user.`)} {i18n._(t`Failed to delete user.`)}
<ErrorDetail error={deletionError} /> <ErrorDetail error={error} />
</AlertModal> </AlertModal>
)} )}
</CardBody> </CardBody>