diff --git a/awx/ui_next/src/screens/Template/shared/SurveyList.jsx b/awx/ui_next/src/screens/Template/shared/SurveyList.jsx index f205c2b0cd..abc5a2e59e 100644 --- a/awx/ui_next/src/screens/Template/shared/SurveyList.jsx +++ b/awx/ui_next/src/screens/Template/shared/SurveyList.jsx @@ -10,23 +10,20 @@ import ContentLoading from '@components/ContentLoading'; import ErrorDetail from '@components/ErrorDetail'; import { JobTemplatesAPI } from '@api'; import ContentEmpty from '@components/ContentEmpty'; -import { getQSConfig } from '@util/qs'; import AlertModal from '@components/AlertModal'; import SurveyListItem from './SurveyListItem'; import SurveyToolbar from './SurveyToolbar'; -const QS_CONFIG = getQSConfig('survey', { - page: 1, -}); - function SurveyList({ template, i18n }) { const [selected, setSelected] = useState([]); + // const [updated, setUpdated] = useState(null); const [surveyEnabled, setSurveyEnabled] = useState(template.survey_enabled); - const [showToggleError, setShowToggleError] = useState(false); + const [showError, setShowError] = useState(false); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const [questions, setQuestions] = useState(null); const { - result: { questions, name, description }, + result: { fetchedQuestions, name, description }, error: contentError, isLoading, request: fetchSurvey, @@ -35,8 +32,9 @@ function SurveyList({ template, i18n }) { const { data: { spec = [], description: surveyDescription, name: surveyName }, } = await JobTemplatesAPI.readSurvey(template.id); + return { - questions: spec?.map((s, index) => ({ ...s, id: index })), + fetchedQuestions: spec?.map((s, index) => ({ ...s, id: index })), description: surveyDescription, name: surveyName, }; @@ -44,41 +42,27 @@ function SurveyList({ template, i18n }) { { questions: [], name: '', description: '' } ); + useEffect(() => { + setQuestions(fetchedQuestions); + }, [fetchedQuestions]); + useEffect(() => { fetchSurvey(); }, [fetchSurvey]); const isAllSelected = selected.length === questions?.length && selected.length > 0; - const { isLoading: isDeleteLoading, - deleteItems: deleteQuestions, + deleteItems: deleteSurvey, deletionError, - clearDeletionError, } = useDeleteItems( useCallback(async () => { - if (isAllSelected) { - return JobTemplatesAPI.destroySurvey(template.id); - } - const surveyQuestions = []; - questions.forEach(q => { - if (!selected.some(s => s.id === q.id)) { - surveyQuestions.push(q); - } - }); - return JobTemplatesAPI.updateSurvey(template.id, { - name, - description, - spec: surveyQuestions, - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selected]), - { - qsConfig: QS_CONFIG, - fetchItems: fetchSurvey, - } + await JobTemplatesAPI.destroySurvey(template.id); + fetchSurvey(); + }, [template.id, fetchSurvey]) ); + const { isToggleLoading, error: toggleError, @@ -93,12 +77,6 @@ function SurveyList({ template, i18n }) { template.survey_enabled ); - useEffect(() => { - if (toggleError) { - setShowToggleError(true); - } - }, [toggleError]); - const handleSelectAll = isSelected => { setSelected(isSelected ? [...questions] : []); }; @@ -112,15 +90,56 @@ function SurveyList({ template, i18n }) { }; const handleDelete = async () => { - await deleteQuestions(); - setIsDeleteModalOpen(false); + if (isAllSelected) { + await deleteSurvey(); + setIsDeleteModalOpen(false); + } else { + setQuestions(questions.filter(q => !selected.includes(q))); + setIsDeleteModalOpen(false); + } setSelected([]); }; - const canEdit = template.summary_fields.user_capabilities.edit; - const canDelete = template.summary_fields.user_capabilities.delete; + + const { + isLoading: isUpdateLoading, + error: updateError, + // request: updateSurvey, + } = useRequest( + useCallback(async () => { + return JobTemplatesAPI.updateSurvey(template.id, { + name, + description, + spec: questions, + }); + }, [template.id, name, description, questions]) + ); + + const moveUp = question => { + const index = questions.indexOf(question); + if (index < 1) { + return; + } + const beginning = questions.slice(0, index - 1); + const swapWith = questions[index - 1]; + const end = questions.slice(index + 1); + setQuestions([...beginning, question, swapWith, ...end]); + }; + const moveDown = question => { + const index = questions.indexOf(question); + if (index === -1 || index > questions.length - 1) { + return; + } + const beginning = questions.slice(0, index); + const swapWith = questions[index + 1]; + const end = questions.slice(index + 2); + setQuestions([...beginning, swapWith, question, ...end]); + }; let content; - if (isLoading || isToggleLoading || isDeleteLoading) { + if ( + (isLoading || isToggleLoading || isDeleteLoading || isUpdateLoading) && + questions?.length <= 0 + ) { content = ; } else if (contentError) { content = ; @@ -140,9 +159,29 @@ function SurveyList({ template, i18n }) { question={question} isChecked={selected.some(s => s.id === question.id)} onSelect={() => handleSelect(question)} + onMoveUp={moveUp} + onMoveDown={moveDown} /> )); } + + const error = deletionError || toggleError || updateError; + let errorMessage = ''; + if (updateError) { + errorMessage = i18n._(t`Failed to update survey`); + } + if (toggleError) { + errorMessage = i18n._(t`Failed to toggle survey`); + } + if (deletionError) { + errorMessage = i18n._(t`Failed to delete survey`); + } + useEffect(() => { + if (error) { + setShowError(true); + } + }, [error]); + return ( <> setIsDeleteModalOpen(true)} /> {content} @@ -198,26 +237,15 @@ function SurveyList({ template, i18n }) { ))} )} - {deletionError && ( + {showError && ( setShowError(false)} > - {i18n._(t`Failed to delete one or more jobs.`)} - - - )} - {toggleError && ( - setShowToggleError(false)} - > - {i18n._(t`Failed to toggle host.`)} - + {errorMessage} + )} diff --git a/awx/ui_next/src/screens/Template/shared/SurveyListItem.jsx b/awx/ui_next/src/screens/Template/shared/SurveyListItem.jsx index ca99cf17f7..9181230419 100644 --- a/awx/ui_next/src/screens/Template/shared/SurveyListItem.jsx +++ b/awx/ui_next/src/screens/Template/shared/SurveyListItem.jsx @@ -35,6 +35,8 @@ function SurveyListItem({ isFirst, isChecked, onSelect, + onMoveUp, + onMoveDown, }) { return ( @@ -51,6 +53,7 @@ function SurveyListItem({ variant="plain" aria-label={i18n._(t`move up`)} isDisabled={isFirst} + onClick={() => onMoveUp(question)} > @@ -60,6 +63,7 @@ function SurveyListItem({ variant="plain" aria-label={i18n._(t`move down`)} isDisabled={isLast} + onClick={() => onMoveDown(question)} > diff --git a/awx/ui_next/src/util/useRequest.js b/awx/ui_next/src/util/useRequest.js index ad8ed747ba..906f3160be 100644 --- a/awx/ui_next/src/util/useRequest.js +++ b/awx/ui_next/src/util/useRequest.js @@ -56,7 +56,7 @@ export default function useRequest(makeRequest, initialValue) { export function useDeleteItems( makeRequest, - { qsConfig, allItemsSelected, fetchItems } + { qsConfig = null, allItemsSelected = false, fetchItems = null } = {} ) { const location = useLocation(); const history = useHistory(); @@ -71,6 +71,9 @@ export function useDeleteItems( const deleteItems = async () => { await request(); + if (!qsConfig) { + return; + } const params = parseQueryString(qsConfig, location.search); if (params.page > 1 && allItemsSelected) { const newParams = encodeNonDefaultQueryString(