From 91ae0ff7dfa9838e774c6d3c69b112ee79b25198 Mon Sep 17 00:00:00 2001 From: Thibault Dethier Date: Thu, 19 Sep 2024 11:10:13 +0200 Subject: [PATCH 01/22] [IA-3443] OUCRC create - WIP --- .../OrgUnitChangeRequestConfigDialog.tsx | 200 ++++++++++++++++++ .../OrgUnitChangeRequestConfigs.tsx | 11 +- ...AvailabilityOrgUnitChangeRequestConfigs.ts | 47 ++++ .../useDeleteOrgUnitChangeRequestConfig.ts | 4 +- .../api/useGetOrgUnitChangeRequestConfigs.ts | 1 + .../orgUnits/configuration/messages.ts | 12 ++ .../domains/orgUnits/configuration/types.ts | 65 +++++- 7 files changed, 330 insertions(+), 10 deletions(-) create mode 100644 hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx create mode 100644 hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useCheckAvailabilityOrgUnitChangeRequestConfigs.ts diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx new file mode 100644 index 0000000000..2c90081684 --- /dev/null +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx @@ -0,0 +1,200 @@ +import React, { FunctionComponent, useCallback, useState } from 'react'; +import { + SimpleModal, + IntlMessage, + makeFullModal, + useSafeIntl, + AddButton, +} from 'bluesquare-components'; +import { Button } from '@mui/material'; +import MESSAGES from '../messages'; +import { OrgUnitChangeRequestConfigurationFull } from '../types'; +import { useGetProjectsDropdownOptions } from '../../../projects/hooks/requests'; +import InputComponent from '../../../../components/forms/InputComponent'; +import { formatLabel } from '../../../instances/utils'; +import { useFormState } from '../../../../hooks/form'; +import { OrgunitType } from '../../types/orgunitTypes'; +import { commaSeparatedIdsToArray, isFieldValid } from '../../../../utils/forms'; +import { requiredFields } from '../../orgUnitTypes/config/requiredFields'; +import { + useCheckAvailabilityOrgUnitChangeRequestConfigs +} from '../hooks/api/useCheckAvailabilityOrgUnitChangeRequestConfigs'; +import * as Yup from 'yup'; +import { useFormik } from 'formik'; +import { getRangeValues, getScaleThreshold } from '../../../../components/LegendBuilder/utils'; +import { useTranslatedErrors } from '../../../../libs/validation'; +import messages from '../messages'; + +type CancelButtonProps = { + closeDialog: () => void; +}; + +const CloseButton: FunctionComponent = ({ closeDialog }) => { + const { formatMessage } = useSafeIntl(); + return ( + + ); +}; + +type Props = { + titleMessage: IntlMessage; + isOpen: boolean; + closeDialog: () => void; + // config?: OrgUnitChangeRequestConfigurationFull; +}; + +const defaultOrgUnitChangeRequestConfig: Omit< + OrgUnitChangeRequestConfigurationFull, + 'id' | 'created_at' | 'updated_at' | 'created_by' | 'updated_by' +> & { + id: undefined; +} = { + id: undefined, + project: undefined, + org_unit_type: undefined, +}; + +const mapConfigForCreation = orgUnitChangeRequestConfig => { + return { + project_id: orgUnitChangeRequestConfig.project.id, + org_unit_type_id: orgUnitChangeRequestConfig.org_unit_type.id, + org_units_editable: orgUnitChangeRequestConfig.org_units_editable, + editable_fields: orgUnitChangeRequestConfig.editable_fields, + possible_type_ids: orgUnitChangeRequestConfig.possible_type_ids.map(type => type.id), + possible_parent_type_ids: orgUnitChangeRequestConfig.possible_parent_type_ids.map(type => type.id), + group_set_ids: orgUnitChangeRequestConfig.group_sets.map(group_set => group_set.id), + editable_reference_form_ids: orgUnitChangeRequestConfig.editable_reference_forms.map(form => form.id), + other_group_ids: orgUnitChangeRequestConfig.other_groups.map(group => group.id), + }; +}; + +const mapConfigForUpdate = orgUnitChangeRequestConfig => { + return { + org_units_editable: orgUnitChangeRequestConfig.org_units_editable, + editable_fields: orgUnitChangeRequestConfig.editable_fields, + possible_type_ids: orgUnitChangeRequestConfig.possible_type_ids.map(type => type.id), + possible_parent_type_ids: orgUnitChangeRequestConfig.possible_parent_type_ids.map(type => type.id), + group_set_ids: orgUnitChangeRequestConfig.group_sets.map(group_set => group_set.id), + editable_reference_form_ids: orgUnitChangeRequestConfig.editable_reference_forms.map(form => form.id), + other_group_ids: orgUnitChangeRequestConfig.other_groups.map(group => group.id), + }; +}; + +const useCreationSchema = () => { + const { formatMessage } = useSafeIntl(); + return Yup.object().shape({ + projectId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), + orgUnitTypeId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), + }); +}; + +const OrgUnitChangeRequestConfigDialog: FunctionComponent = ({ + titleMessage, + isOpen, + closeDialog, +}) => { + + const creationSchema = useCreationSchema(); + const { + values, + setFieldValue, + setFieldError, + isValid, + handleSubmit, + errors, + touched, + setFieldTouched, + } = useFormik({ + initialValues: { + projectId: undefined, + orgUnitTypeId: undefined, + }, + validationSchema: creationSchema, + onSubmit: () => { + console.log("*** onSubmit values = ", values); + }, + }); + + const { formatMessage } = useSafeIntl(); + const getErrors = useTranslatedErrors({ + errors, + touched, + formatMessage, + messages: MESSAGES, + }); + + + const { data: allProjects, isFetching: isFetchingProjects } = useGetProjectsDropdownOptions(); + const { data: availableOrgUnitTypes, isFetching: isFetchingOrgUnitTypes } = useCheckAvailabilityOrgUnitChangeRequestConfigs(formState.project.id); + + const onChange = useCallback( + (keyValue, value) => { + setFieldTouched(keyValue, true); + if ( + keyValue === 'possible_type_ids' || + keyValue === 'possible_parent_type_ids' || + keyValue === 'group_set_ids' || + keyValue === 'editable_reference_form_ids' || + keyValue === 'other_group_ids' + ) { + setFieldValue(keyValue, commaSeparatedIdsToArray(value)); + } else { + setFieldValue(keyValue, value); + } + }, + [setFieldValue, formatMessage], + ); + + console.log("*** render values = ", values); + + return ( + null} + id="PaymentLotEditionDialog" + dataTestId="PaymentLotEditionDialog" + titleMessage={titleMessage} + closeDialog={closeDialog} + maxWidth="md" + > + + + + ); +}; + +const modalWithButton = makeFullModal( + OrgUnitChangeRequestConfigDialog, + AddButton, +); + +export { modalWithButton as OrgUnitChangeRequestConfigDialog }; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx index dbc75459e4..3acbd191f9 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx @@ -10,6 +10,7 @@ import TopBar from '../../../components/nav/TopBarComponent'; import MESSAGES from './messages'; import { OrgUnitChangeRequestConfigsFilter } from './Filter/OrgUnitChangeRequestConfigsFilter'; import { OrgUnitChangeRequestConfigsTable } from './Tables/OrgUnitChangeRequestConfigsTable'; +import { OrgUnitChangeRequestConfigDialog } from './Dialog/OrgUnitChangeRequestConfigDialog'; const useStyles = makeStyles(theme => ({ ...commonStyles(theme), @@ -24,7 +25,6 @@ export const OrgUnitChangeRequestConfigs: FunctionComponent = () => { const classes: Record = useStyles(); const { formatMessage } = useSafeIntl(); - return (
{ - { - // TODO - }} /> @@ -47,7 +46,7 @@ export const OrgUnitChangeRequestConfigs: FunctionComponent = () => { data={data} isFetching={isFetching} params={params} - onEditClicked={(config) => { + onEditClicked={config => { // TODO }} /> diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useCheckAvailabilityOrgUnitChangeRequestConfigs.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useCheckAvailabilityOrgUnitChangeRequestConfigs.ts new file mode 100644 index 0000000000..b7dc740a0b --- /dev/null +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useCheckAvailabilityOrgUnitChangeRequestConfigs.ts @@ -0,0 +1,47 @@ +import { UseQueryResult } from 'react-query'; +import { makeUrlWithParams } from '../../../../../libs/utils'; +import { getRequest } from '../../../../../libs/Api'; +import { useSnackQuery } from '../../../../../libs/apiHooks'; + +import { useLocale } from '../../../../app/contexts/LocaleContext'; +import { apiUrl } from '../../constants'; +import { + CheckAvailiabilityOrgUnitRequestConfig, + OrgUnitChangeRequestConfigsParams, OrgUnitType, +} from '../../types'; +import { DropdownOptions } from '../../../../../types/utils'; + +const checkAvailabilityOrgUnitChangeRequestConfigs = (url: string) => { + return getRequest(url) as Promise; +}; + +export const useCheckAvailabilityOrgUnitChangeRequestConfigs = ( + params: OrgUnitChangeRequestConfigsParams, +): UseQueryResult, Error> => { + const { locale } = useLocale(); + const apiParams = { + project_id: params.project_id, + }; + + const url = makeUrlWithParams(`${apiUrl}check_availability/`, apiParams); + return useSnackQuery({ + // Including locale in the query key because we need to make a call to update translations coming from the backend + queryKey: ['checkAvailabilityOrgUnitChangeRequestConfigs', url, locale], + queryFn: () => checkAvailabilityOrgUnitChangeRequestConfigs(url), + options: { + staleTime: 1000 * 60 * 15, // in MS + cacheTime: 1000 * 60 * 5, + keepPreviousData: true, + select: data => { + return ( + data?.results?.map((orgUnitType: OrgUnitType) => { + return { + value: orgUnitType.id, + label: orgUnitType.name, + }; + }) ?? [] + ); + }, + }, + }); +}; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useDeleteOrgUnitChangeRequestConfig.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useDeleteOrgUnitChangeRequestConfig.ts index f1834fa0b0..39ff692528 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useDeleteOrgUnitChangeRequestConfig.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useDeleteOrgUnitChangeRequestConfig.ts @@ -5,7 +5,9 @@ import { apiUrl } from '../../constants'; import { OrgUnitChangeRequestConfig } from '../../types'; import { deleteRequest } from '../../../../../libs/Api'; -const deleteOrgUnitChangeRequestConfigs = (config: OrgUnitChangeRequestConfig) => { +const deleteOrgUnitChangeRequestConfigs = ( + config: OrgUnitChangeRequestConfig, +) => { return deleteRequest(`${apiUrl}/${config.id}/`) as Promise; }; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOrgUnitChangeRequestConfigs.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOrgUnitChangeRequestConfigs.ts index e45fbcf509..99ba4fd81f 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOrgUnitChangeRequestConfigs.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOrgUnitChangeRequestConfigs.ts @@ -35,6 +35,7 @@ export const useGetOrgUnitChangeRequestConfigs = ( staleTime: 1000 * 60 * 15, // in MS cacheTime: 1000 * 60 * 5, keepPreviousData: true, + retry: false, }, }); }; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts index 54d4417198..7cfbc61970 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts @@ -61,6 +61,18 @@ const MESSAGES = defineMessages({ id: 'iaso.label.updated_by', defaultMessage: 'Updated by', }, + close: { + id: 'iaso.label.close', + defaultMessage: 'Close hihi', + }, + oucrcCreateModalTitle: { + id: 'iaso.label.oucrcCreateModalTitle', + defaultMessage: 'OrgUnit Change Request Configuration - Creation', + }, + requiredField: { + id: 'iaso.forms.error.fieldRequired', + defaultMessage: 'This field is required', + }, }); export default MESSAGES; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts index 257efe5710..24c5531a5f 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts @@ -15,9 +15,25 @@ export type OrgUnitType = { id: number; name: string; }; + +export type GroupSet = { + id: number; + name: string; +}; + +export type Form = { + id: number; + name: string; +}; + +export type Group = { + id: number; + name: string; +}; + export type NestedUser = Partial; -export type OrgUnitChangeRequestConfig = { +export type OrgUnitChangeRequestConfigListElement = { id: number; project: Project; org_unit_type: OrgUnitType; @@ -29,6 +45,49 @@ export type OrgUnitChangeRequestConfig = { updated_at: number; }; -export interface OrgUnitChangeRequestConfigsPaginated extends Pagination { - results: OrgUnitChangeRequestConfig[]; +export type OrgUnitChangeRequestConfigurationFull = { + id?: number; + project?: Project; + org_unit_type?: OrgUnitType; + org_units_editable?: boolean; + editable_fields?: Array; + possible_types?: Array; + possible_parent_types?: Array; + group_sets?: Array; + editable_reference_forms?: Array
; + other_groups?: Array; + created_by?: NestedUser; + created_at?: number; + updated_by?: NestedUser; + updated_at?: number; +}; + +export type OrgUnitChangeRequestConfigurationCreate = { + project_id: number; + org_unit_type_id: number; + org_units_editable?: boolean; + editable_fields?: Array; + possible_type_ids?: Array; + possible_parent_type_ids?: Array; + group_set_ids?: Array; + editable_reference_form_ids?: Array; + other_group_ids?: Array; }; + +export type OrgUnitChangeRequestConfigurationUpdate = { + org_units_editable?: boolean; + editable_fields?: Array; + possible_type_ids?: Array; + possible_parent_type_ids?: Array; + group_set_ids?: Array; + editable_reference_form_ids?: Array; + other_group_ids?: Array; +}; + +export interface OrgUnitChangeRequestConfigsPaginated extends Pagination { + results: OrgUnitChangeRequestConfigListElement[]; +} + +export interface CheckAvailiabilityOrgUnitRequestConfig { + results: OrgUnitType[]; +} From 2ddd07203225b29c9ba673457346c30682b13b49 Mon Sep 17 00:00:00 2001 From: Thibault Dethier Date: Thu, 19 Sep 2024 12:27:05 +0200 Subject: [PATCH 02/22] [IA-3443] OUCRC create - WIP --- .../OrgUnitChangeRequestConfigDialog.tsx | 200 ------------------ ...OrgUnitChangeRequestConfigDialogCreate.tsx | 135 ++++++++++++ ...OrgUnitChangeRequestConfigDialogUpdate.tsx | 129 +++++++++++ .../OrgUnitChangeRequestConfigs.tsx | 23 +- ...AvailabilityOrgUnitChangeRequestConfigs.ts | 27 +-- .../orgUnits/configuration/messages.ts | 6 +- 6 files changed, 292 insertions(+), 228 deletions(-) delete mode 100644 hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx create mode 100644 hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreate.tsx create mode 100644 hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx deleted file mode 100644 index 2c90081684..0000000000 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx +++ /dev/null @@ -1,200 +0,0 @@ -import React, { FunctionComponent, useCallback, useState } from 'react'; -import { - SimpleModal, - IntlMessage, - makeFullModal, - useSafeIntl, - AddButton, -} from 'bluesquare-components'; -import { Button } from '@mui/material'; -import MESSAGES from '../messages'; -import { OrgUnitChangeRequestConfigurationFull } from '../types'; -import { useGetProjectsDropdownOptions } from '../../../projects/hooks/requests'; -import InputComponent from '../../../../components/forms/InputComponent'; -import { formatLabel } from '../../../instances/utils'; -import { useFormState } from '../../../../hooks/form'; -import { OrgunitType } from '../../types/orgunitTypes'; -import { commaSeparatedIdsToArray, isFieldValid } from '../../../../utils/forms'; -import { requiredFields } from '../../orgUnitTypes/config/requiredFields'; -import { - useCheckAvailabilityOrgUnitChangeRequestConfigs -} from '../hooks/api/useCheckAvailabilityOrgUnitChangeRequestConfigs'; -import * as Yup from 'yup'; -import { useFormik } from 'formik'; -import { getRangeValues, getScaleThreshold } from '../../../../components/LegendBuilder/utils'; -import { useTranslatedErrors } from '../../../../libs/validation'; -import messages from '../messages'; - -type CancelButtonProps = { - closeDialog: () => void; -}; - -const CloseButton: FunctionComponent = ({ closeDialog }) => { - const { formatMessage } = useSafeIntl(); - return ( - - ); -}; - -type Props = { - titleMessage: IntlMessage; - isOpen: boolean; - closeDialog: () => void; - // config?: OrgUnitChangeRequestConfigurationFull; -}; - -const defaultOrgUnitChangeRequestConfig: Omit< - OrgUnitChangeRequestConfigurationFull, - 'id' | 'created_at' | 'updated_at' | 'created_by' | 'updated_by' -> & { - id: undefined; -} = { - id: undefined, - project: undefined, - org_unit_type: undefined, -}; - -const mapConfigForCreation = orgUnitChangeRequestConfig => { - return { - project_id: orgUnitChangeRequestConfig.project.id, - org_unit_type_id: orgUnitChangeRequestConfig.org_unit_type.id, - org_units_editable: orgUnitChangeRequestConfig.org_units_editable, - editable_fields: orgUnitChangeRequestConfig.editable_fields, - possible_type_ids: orgUnitChangeRequestConfig.possible_type_ids.map(type => type.id), - possible_parent_type_ids: orgUnitChangeRequestConfig.possible_parent_type_ids.map(type => type.id), - group_set_ids: orgUnitChangeRequestConfig.group_sets.map(group_set => group_set.id), - editable_reference_form_ids: orgUnitChangeRequestConfig.editable_reference_forms.map(form => form.id), - other_group_ids: orgUnitChangeRequestConfig.other_groups.map(group => group.id), - }; -}; - -const mapConfigForUpdate = orgUnitChangeRequestConfig => { - return { - org_units_editable: orgUnitChangeRequestConfig.org_units_editable, - editable_fields: orgUnitChangeRequestConfig.editable_fields, - possible_type_ids: orgUnitChangeRequestConfig.possible_type_ids.map(type => type.id), - possible_parent_type_ids: orgUnitChangeRequestConfig.possible_parent_type_ids.map(type => type.id), - group_set_ids: orgUnitChangeRequestConfig.group_sets.map(group_set => group_set.id), - editable_reference_form_ids: orgUnitChangeRequestConfig.editable_reference_forms.map(form => form.id), - other_group_ids: orgUnitChangeRequestConfig.other_groups.map(group => group.id), - }; -}; - -const useCreationSchema = () => { - const { formatMessage } = useSafeIntl(); - return Yup.object().shape({ - projectId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), - orgUnitTypeId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), - }); -}; - -const OrgUnitChangeRequestConfigDialog: FunctionComponent = ({ - titleMessage, - isOpen, - closeDialog, -}) => { - - const creationSchema = useCreationSchema(); - const { - values, - setFieldValue, - setFieldError, - isValid, - handleSubmit, - errors, - touched, - setFieldTouched, - } = useFormik({ - initialValues: { - projectId: undefined, - orgUnitTypeId: undefined, - }, - validationSchema: creationSchema, - onSubmit: () => { - console.log("*** onSubmit values = ", values); - }, - }); - - const { formatMessage } = useSafeIntl(); - const getErrors = useTranslatedErrors({ - errors, - touched, - formatMessage, - messages: MESSAGES, - }); - - - const { data: allProjects, isFetching: isFetchingProjects } = useGetProjectsDropdownOptions(); - const { data: availableOrgUnitTypes, isFetching: isFetchingOrgUnitTypes } = useCheckAvailabilityOrgUnitChangeRequestConfigs(formState.project.id); - - const onChange = useCallback( - (keyValue, value) => { - setFieldTouched(keyValue, true); - if ( - keyValue === 'possible_type_ids' || - keyValue === 'possible_parent_type_ids' || - keyValue === 'group_set_ids' || - keyValue === 'editable_reference_form_ids' || - keyValue === 'other_group_ids' - ) { - setFieldValue(keyValue, commaSeparatedIdsToArray(value)); - } else { - setFieldValue(keyValue, value); - } - }, - [setFieldValue, formatMessage], - ); - - console.log("*** render values = ", values); - - return ( - null} - id="PaymentLotEditionDialog" - dataTestId="PaymentLotEditionDialog" - titleMessage={titleMessage} - closeDialog={closeDialog} - maxWidth="md" - > - - - - ); -}; - -const modalWithButton = makeFullModal( - OrgUnitChangeRequestConfigDialog, - AddButton, -); - -export { modalWithButton as OrgUnitChangeRequestConfigDialog }; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreate.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreate.tsx new file mode 100644 index 0000000000..cd96ea1528 --- /dev/null +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreate.tsx @@ -0,0 +1,135 @@ +import React, { FunctionComponent, useCallback } from 'react'; +import { + ConfirmCancelModal, + makeFullModal, + useSafeIntl, + AddButton, +} from 'bluesquare-components'; +import * as Yup from 'yup'; +import { useFormik } from 'formik'; +import { useTranslatedErrors } from '../../../../libs/validation'; +import MESSAGES from '../messages'; +import { useGetProjectsDropdownOptions } from '../../../projects/hooks/requests'; +import InputComponent from '../../../../components/forms/InputComponent'; +import { + useCheckAvailabilityOrgUnitChangeRequestConfigs, +} from '../hooks/api/useCheckAvailabilityOrgUnitChangeRequestConfigs'; +import { isEqual } from 'lodash'; + +type Props = { + isOpen: boolean; + closeDialog: () => void; + openUpdateDialog: () => void; +}; + +const useCreationSchema = () => { + const { formatMessage } = useSafeIntl(); + return Yup.object().shape({ + projectId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), + orgUnitTypeId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), + }); +}; + +const OrgUnitChangeRequestConfigDialogCreate: FunctionComponent = ({ + isOpen, + closeDialog, + openUpdateDialog, +}) => { + const creationSchema = useCreationSchema(); + const { + values, + setFieldValue, + isValid, + handleSubmit, + isSubmitting, + errors, + touched, + setFieldTouched, + } = useFormik({ + initialValues: { + projectId: undefined, + orgUnitTypeId: undefined, + }, + validationSchema: creationSchema, + onSubmit: () => { + console.log('*** onSubmit values = ', values); + openUpdateDialog(); + }, + }); + + console.log("*** createDialog - openUpdateDialog = ", openUpdateDialog); + + const { formatMessage } = useSafeIntl(); + const getErrors = useTranslatedErrors({ + errors, + touched, + formatMessage, + messages: MESSAGES, + }); + + const { data: allProjects, isFetching: isFetchingProjects } = useGetProjectsDropdownOptions(); + const { + data: availableOrgUnitTypes, + isFetching: isFetchingOrgUnitTypes, + } = useCheckAvailabilityOrgUnitChangeRequestConfigs(values.projectId); + + const onChange = useCallback( + (keyValue, value) => { + setFieldTouched(keyValue, true); + setFieldValue(keyValue, value); + }, + [setFieldValue, setFieldTouched], + ); + + // const allowConfirm = isValid && !isSubmitting && !isEqual(touched, {}); + const allowConfirm = true; + + return ( + null} + id="oucrcDialogCreate" + dataTestId="add-org-unit-config-button" + titleMessage={formatMessage(MESSAGES.oucrcCreateModalTitle)} + closeDialog={closeDialog} + maxWidth="xs" + allowConfirm={allowConfirm} + cancelMessage={MESSAGES.cancel} + confirmMessage={MESSAGES.next} + onConfirm={() => handleSubmit()} + onCancel={() => { + closeDialog(); + }} + > + + + + ); +}; + +const modalWithButton = makeFullModal( + OrgUnitChangeRequestConfigDialogCreate, + AddButton, +); + +export { modalWithButton as OrgUnitChangeRequestConfigDialogCreate }; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx new file mode 100644 index 0000000000..0f6bb7858d --- /dev/null +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx @@ -0,0 +1,129 @@ +import React, { FunctionComponent, useCallback } from 'react'; +import { + ConfirmCancelModal, + makeFullModal, + useSafeIntl, +} from 'bluesquare-components'; +import * as Yup from 'yup'; +import { useFormik } from 'formik'; +import { useTranslatedErrors } from '../../../../libs/validation'; +import MESSAGES from '../messages'; +import { useGetProjectsDropdownOptions } from '../../../projects/hooks/requests'; +import { EditIconButton } from '../../../../components/Buttons/EditIconButton'; +import InputComponent from '../../../../components/forms/InputComponent'; +import { + useCheckAvailabilityOrgUnitChangeRequestConfigs, +} from '../hooks/api/useCheckAvailabilityOrgUnitChangeRequestConfigs'; +import { isEqual } from 'lodash'; + +type Props = { + isOpen: boolean; + closeDialog: () => void; +}; + +const useCreationSchema = () => { + const { formatMessage } = useSafeIntl(); + return Yup.object().shape({ + projectId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), + orgUnitTypeId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), + }); +}; + +const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ + isOpen, + closeDialog, +}) => { + const creationSchema = useCreationSchema(); + const { + values, + setFieldValue, + isValid, + handleSubmit, + isSubmitting, + errors, + touched, + setFieldTouched, + } = useFormik({ + initialValues: { + projectId: undefined, + orgUnitTypeId: undefined, + }, + validationSchema: creationSchema, + onSubmit: () => { + console.log('*** onSubmit values = ', values); + }, + }); + + const { formatMessage } = useSafeIntl(); + const getErrors = useTranslatedErrors({ + errors, + touched, + formatMessage, + messages: MESSAGES, + }); + + const { data: allProjects, isFetching: isFetchingProjects } = useGetProjectsDropdownOptions(); + const { + data: availableOrgUnitTypes, + isFetching: isFetchingOrgUnitTypes, + } = useCheckAvailabilityOrgUnitChangeRequestConfigs(values.projectId); + + const onChange = useCallback( + (keyValue, value) => { + setFieldTouched(keyValue, true); + setFieldValue(keyValue, value); + }, + [setFieldValue, setFieldTouched], + ); + + const allowConfirm = isValid && !isSubmitting && !isEqual(touched, {}); + + return ( + null} + id="oucrcDialogCreate" + dataTestId="add-org-unit-config-button" + titleMessage={formatMessage(MESSAGES.oucrcCreateModalTitle)} + closeDialog={closeDialog} + maxWidth="xs" + allowConfirm={allowConfirm} + cancelMessage={MESSAGES.cancel} + confirmMessage={MESSAGES.next} + onConfirm={() => handleSubmit()} + onCancel={() => { + closeDialog(); + }} + > + + + + ); +}; + +const modalWithButton = makeFullModal( + OrgUnitChangeRequestConfigDialogUpdate, + EditIconButton, +); + +export { modalWithButton as OrgUnitChangeRequestConfigDialogUpdateWithButton, OrgUnitChangeRequestConfigDialogUpdate }; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx index 3acbd191f9..228045832f 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx @@ -1,6 +1,6 @@ import { makeStyles } from '@mui/styles'; -import { AddButton, commonStyles, useSafeIntl } from 'bluesquare-components'; -import React, { FunctionComponent } from 'react'; +import { commonStyles, useSafeIntl } from 'bluesquare-components'; +import React, { FunctionComponent, useState } from 'react'; import { Box } from '@mui/material'; import { useParamsObject } from '../../../routing/hooks/useParamsObject'; import { baseUrls } from '../../../constants/urls'; @@ -10,7 +10,8 @@ import TopBar from '../../../components/nav/TopBarComponent'; import MESSAGES from './messages'; import { OrgUnitChangeRequestConfigsFilter } from './Filter/OrgUnitChangeRequestConfigsFilter'; import { OrgUnitChangeRequestConfigsTable } from './Tables/OrgUnitChangeRequestConfigsTable'; -import { OrgUnitChangeRequestConfigDialog } from './Dialog/OrgUnitChangeRequestConfigDialog'; +import { OrgUnitChangeRequestConfigDialogCreate } from './Dialog/OrgUnitChangeRequestConfigDialogCreate'; +import { OrgUnitChangeRequestConfigDialogUpdate } from './Dialog/OrgUnitChangeRequestConfigDialogUpdate'; const useStyles = makeStyles(theme => ({ ...commonStyles(theme), @@ -21,10 +22,14 @@ export const OrgUnitChangeRequestConfigs: FunctionComponent = () => { baseUrls.orgUnitsChangeRequestConfiguration, ) as unknown as OrgUnitChangeRequestConfigsParams; const { data, isFetching } = useGetOrgUnitChangeRequestConfigs(params); + const [isUpdateDialogOpen, setIsUpdateDialogOpen] = + useState(false); const classes: Record = useStyles(); const { formatMessage } = useSafeIntl(); + console.log("*** state isDialogOpen = ", isUpdateDialogOpen); + return (
{ - { + console.log("*** openUpdateDialog ***"); + setIsUpdateDialogOpen(true); + }} /> + { setIsUpdateDialogOpen(false) }} /> { - return getRequest(url) as Promise; -}; - export const useCheckAvailabilityOrgUnitChangeRequestConfigs = ( - params: OrgUnitChangeRequestConfigsParams, -): UseQueryResult, Error> => { - const { locale } = useLocale(); - const apiParams = { - project_id: params.project_id, - }; - - const url = makeUrlWithParams(`${apiUrl}check_availability/`, apiParams); + projectId?: number, +): UseQueryResult[], Error> => { + const url = `${apiUrl}check_availability/?project_id=${projectId}`; return useSnackQuery({ // Including locale in the query key because we need to make a call to update translations coming from the backend - queryKey: ['checkAvailabilityOrgUnitChangeRequestConfigs', url, locale], - queryFn: () => checkAvailabilityOrgUnitChangeRequestConfigs(url), + queryKey: ['checkAvailabilityOrgUnitChangeRequestConfigs', url], + queryFn: () => getRequest(url), options: { + enabled: Boolean(projectId), staleTime: 1000 * 60 * 15, // in MS cacheTime: 1000 * 60 * 5, keepPreviousData: true, diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts index 7cfbc61970..9f833d03de 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts @@ -63,7 +63,11 @@ const MESSAGES = defineMessages({ }, close: { id: 'iaso.label.close', - defaultMessage: 'Close hihi', + defaultMessage: 'Close', + }, + next: { + id: 'iaso.label.next', + defaultMessage: 'Next', }, oucrcCreateModalTitle: { id: 'iaso.label.oucrcCreateModalTitle', From 855fd46d2bdb81c77f883c8db0591dbbaaddf748 Mon Sep 17 00:00:00 2001 From: Thibault Dethier Date: Fri, 20 Sep 2024 10:03:14 +0200 Subject: [PATCH 03/22] [IA-3443] OUCRC creation - WIP --- ...ngeRequestConfigDialogCreateFirstStep.tsx} | 29 ++- ...OrgUnitChangeRequestConfigDialogUpdate.tsx | 199 ++++++++++++++---- .../OrgUnitChangeRequestConfigs.tsx | 36 +++- .../orgUnits/configuration/constants.ts | 24 ++- .../useDeleteOrgUnitChangeRequestConfig.ts | 8 +- ...onfigs.ts => useGetFormDropdownOptions.ts} | 25 +-- ...etOUCRCCheckAvailabilityDropdownOptions.ts | 46 ++++ .../api/useGetOrgUnitChangeRequestConfigs.ts | 4 +- .../orgUnits/configuration/messages.ts | 72 +++++++ .../domains/orgUnits/configuration/types.ts | 44 +--- 10 files changed, 371 insertions(+), 116 deletions(-) rename hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/{OrgUnitChangeRequestConfigDialogCreate.tsx => OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx} (82%) rename hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/{useCheckAvailabilityOrgUnitChangeRequestConfigs.ts => useGetFormDropdownOptions.ts} (51%) create mode 100644 hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOUCRCCheckAvailabilityDropdownOptions.ts diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreate.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx similarity index 82% rename from hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreate.tsx rename to hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx index cd96ea1528..ff1676efc0 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreate.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx @@ -12,28 +12,30 @@ import MESSAGES from '../messages'; import { useGetProjectsDropdownOptions } from '../../../projects/hooks/requests'; import InputComponent from '../../../../components/forms/InputComponent'; import { - useCheckAvailabilityOrgUnitChangeRequestConfigs, -} from '../hooks/api/useCheckAvailabilityOrgUnitChangeRequestConfigs'; + useGetOUCRCCheckAvailabilityDropdownOptions, +} from '../hooks/api/useGetOUCRCCheckAvailabilityDropdownOptions'; import { isEqual } from 'lodash'; type Props = { isOpen: boolean; closeDialog: () => void; - openUpdateDialog: () => void; + // eslint-disable-next-line no-unused-vars + openCreationSecondStepDialog: (config: object) => void; }; const useCreationSchema = () => { const { formatMessage } = useSafeIntl(); return Yup.object().shape({ projectId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), - orgUnitTypeId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), + // orgUnitTypeId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), + orgUnitTypeId: Yup.string().nullable(), }); }; -const OrgUnitChangeRequestConfigDialogCreate: FunctionComponent = ({ +const OrgUnitChangeRequestConfigDialogCreateFirstStep: FunctionComponent = ({ isOpen, closeDialog, - openUpdateDialog, + openCreationSecondStepDialog, }) => { const creationSchema = useCreationSchema(); const { @@ -52,13 +54,10 @@ const OrgUnitChangeRequestConfigDialogCreate: FunctionComponent = ({ }, validationSchema: creationSchema, onSubmit: () => { - console.log('*** onSubmit values = ', values); - openUpdateDialog(); + openCreationSecondStepDialog(values); }, }); - console.log("*** createDialog - openUpdateDialog = ", openUpdateDialog); - const { formatMessage } = useSafeIntl(); const getErrors = useTranslatedErrors({ errors, @@ -69,9 +68,9 @@ const OrgUnitChangeRequestConfigDialogCreate: FunctionComponent = ({ const { data: allProjects, isFetching: isFetchingProjects } = useGetProjectsDropdownOptions(); const { - data: availableOrgUnitTypes, + data: orgUnitTypeOptions, isFetching: isFetchingOrgUnitTypes, - } = useCheckAvailabilityOrgUnitChangeRequestConfigs(values.projectId); + } = useGetOUCRCCheckAvailabilityDropdownOptions(values.projectId); const onChange = useCallback( (keyValue, value) => { @@ -120,7 +119,7 @@ const OrgUnitChangeRequestConfigDialogCreate: FunctionComponent = ({ onChange={onChange} value={values.orgUnitTypeId} label={MESSAGES.orgUnitType} - options={availableOrgUnitTypes || []} + options={orgUnitTypeOptions || []} errors={getErrors('orgUnitTypeId')} /> @@ -128,8 +127,8 @@ const OrgUnitChangeRequestConfigDialogCreate: FunctionComponent = ({ }; const modalWithButton = makeFullModal( - OrgUnitChangeRequestConfigDialogCreate, + OrgUnitChangeRequestConfigDialogCreateFirstStep, AddButton, ); -export { modalWithButton as OrgUnitChangeRequestConfigDialogCreate }; +export { modalWithButton as OrgUnitChangeRequestConfigDialogCreateFirstStep }; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx index 0f6bb7858d..afeacd9523 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx @@ -1,22 +1,25 @@ -import React, { FunctionComponent, useCallback } from 'react'; +import React, { FunctionComponent, useCallback, useState } from 'react'; import { ConfirmCancelModal, + IntlFormatMessage, makeFullModal, useSafeIntl, } from 'bluesquare-components'; +import { isEqual } from 'lodash'; +import { Typography } from '@mui/material'; import * as Yup from 'yup'; import { useFormik } from 'formik'; import { useTranslatedErrors } from '../../../../libs/validation'; import MESSAGES from '../messages'; -import { useGetProjectsDropdownOptions } from '../../../projects/hooks/requests'; import { EditIconButton } from '../../../../components/Buttons/EditIconButton'; import InputComponent from '../../../../components/forms/InputComponent'; -import { - useCheckAvailabilityOrgUnitChangeRequestConfigs, -} from '../hooks/api/useCheckAvailabilityOrgUnitChangeRequestConfigs'; -import { isEqual } from 'lodash'; +import { editableFields } from '../constants'; +import { useGetOrgUnitTypesDropdownOptions } from '../../orgUnitTypes/hooks/useGetOrgUnitTypesDropdownOptions'; +import { useGetGroupDropdown } from '../../hooks/requests/useGetGroups'; +import { useGetFormDropdownOptions } from '../hooks/api/useGetFormDropdownOptions'; type Props = { + config: object; isOpen: boolean; closeDialog: () => void; }; @@ -25,11 +28,29 @@ const useCreationSchema = () => { const { formatMessage } = useSafeIntl(); return Yup.object().shape({ projectId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), - orgUnitTypeId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), + // orgUnitTypeId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), + orgUnitTypeId: Yup.string().nullable(), + orgUnitsEditable: Yup.boolean().nullable().required(formatMessage(MESSAGES.requiredField)), + editableFields: Yup.array().of(Yup.string()).nullable(), + possibleTypeIds: Yup.array().of(Yup.string()).nullable(), + possibleParentTypeIds: Yup.array().of(Yup.string()).nullable(), + groupSetIds: Yup.array().of(Yup.string()).nullable(), + editableReferenceFormIds: Yup.array().of(Yup.string()).nullable(), + otherGroupIds: Yup.array().of(Yup.string()).nullable(), + }); +}; + +const editableFieldsOptions = (formatMessage: IntlFormatMessage) => { + return editableFields.map(field => { + return { + value: field, + label: formatMessage(MESSAGES[field]), + }; }); }; const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ + config, isOpen, closeDialog, }) => { @@ -45,8 +66,16 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ setFieldTouched, } = useFormik({ initialValues: { - projectId: undefined, - orgUnitTypeId: undefined, + projectId: config.projectId, + // orgUnitTypeId: config.orgUnitTypeId, + orgUnitTypeId: 6, + orgUnitsEditable: '', + editableFields: undefined, + possibleTypeIds: undefined, + possibleParentTypeIds: undefined, + groupSetIds: undefined, + editableReferenceFormIds: undefined, + otherGroupIds: undefined, }, validationSchema: creationSchema, onSubmit: () => { @@ -54,6 +83,11 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ }, }); + const { data: orgUnitTypeOptions, isLoading: isLoadingTypes } = useGetOrgUnitTypesDropdownOptions(); + const { data: groupOptions, isLoading: isLoadingGroups } = useGetGroupDropdown({}); + const { data: formOptions, isFetching: isFetchingForms } = useGetFormDropdownOptions(); + const { data: groupSetOptions, isLoading: isLoadingGroupSets } = useGetGroupDropdown({}); + const { formatMessage } = useSafeIntl(); const getErrors = useTranslatedErrors({ errors, @@ -62,20 +96,18 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ messages: MESSAGES, }); - const { data: allProjects, isFetching: isFetchingProjects } = useGetProjectsDropdownOptions(); - const { - data: availableOrgUnitTypes, - isFetching: isFetchingOrgUnitTypes, - } = useCheckAvailabilityOrgUnitChangeRequestConfigs(values.projectId); - const onChange = useCallback( (keyValue, value) => { + console.log('*** update - onChange - keyValue = ', keyValue); + console.log('*** update - onChange - value = ', value); setFieldTouched(keyValue, true); setFieldValue(keyValue, value); }, [setFieldValue, setFieldTouched], ); + console.log('*** update - values = ', values); + const allowConfirm = isValid && !isSubmitting && !isEqual(touched, {}); return ( @@ -84,39 +116,129 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ onClose={() => null} id="oucrcDialogCreate" dataTestId="add-org-unit-config-button" - titleMessage={formatMessage(MESSAGES.oucrcCreateModalTitle)} + titleMessage={ + config?.id + ? formatMessage(MESSAGES.oucrcCreateUpdateModalTitle) + : formatMessage(MESSAGES.oucrcCreateSecondStepModalTitle) + } closeDialog={closeDialog} maxWidth="xs" allowConfirm={allowConfirm} cancelMessage={MESSAGES.cancel} - confirmMessage={MESSAGES.next} + confirmMessage={ + config?.id + ? MESSAGES.oucrcModalUpdateButton + : MESSAGES.oucrcModalCreateButton + // ? formatMessage(MESSAGES.oucrcModalUpdateButton) + // : formatMessage(MESSAGES.oucrcModalCreateButton) + } onConfirm={() => handleSubmit()} onCancel={() => { closeDialog(); }} > + + Project: {config.projectId} + + + Org Unit Type: {config.orgUnitTypeId} + - + {values?.orgUnitsEditable === 'true' && ( + + )} + {values?.editableFields && values.editableFields.includes("possibleTypes") && ( + + )} + {values?.editableFields && values.editableFields.includes("possibleParentTypes") && ( + + )} + {values?.editableFields && values.editableFields.includes("groupSets") && ( + + )} + {values?.editableFields && values.editableFields.includes("editableReferenceForms") && ( + + )} + {values?.editableFields && values.editableFields.includes("otherGroups") && ( + + )} ); }; @@ -126,4 +248,7 @@ const modalWithButton = makeFullModal( EditIconButton, ); -export { modalWithButton as OrgUnitChangeRequestConfigDialogUpdateWithButton, OrgUnitChangeRequestConfigDialogUpdate }; +export { + modalWithButton as OrgUnitChangeRequestConfigDialogUpdate, + OrgUnitChangeRequestConfigDialogUpdate as OrgUnitChangeRequestConfigDialogCreateSecondStep, +}; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx index 228045832f..543952c8fa 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx @@ -1,6 +1,6 @@ import { makeStyles } from '@mui/styles'; import { commonStyles, useSafeIntl } from 'bluesquare-components'; -import React, { FunctionComponent, useState } from 'react'; +import React, { FunctionComponent, useCallback, useState } from 'react'; import { Box } from '@mui/material'; import { useParamsObject } from '../../../routing/hooks/useParamsObject'; import { baseUrls } from '../../../constants/urls'; @@ -10,8 +10,11 @@ import TopBar from '../../../components/nav/TopBarComponent'; import MESSAGES from './messages'; import { OrgUnitChangeRequestConfigsFilter } from './Filter/OrgUnitChangeRequestConfigsFilter'; import { OrgUnitChangeRequestConfigsTable } from './Tables/OrgUnitChangeRequestConfigsTable'; -import { OrgUnitChangeRequestConfigDialogCreate } from './Dialog/OrgUnitChangeRequestConfigDialogCreate'; -import { OrgUnitChangeRequestConfigDialogUpdate } from './Dialog/OrgUnitChangeRequestConfigDialogUpdate'; +import { OrgUnitChangeRequestConfigDialogCreateFirstStep } from './Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep'; +import { + OrgUnitChangeRequestConfigDialogCreateSecondStep, + OrgUnitChangeRequestConfigDialogUpdate, +} from './Dialog/OrgUnitChangeRequestConfigDialogUpdate'; const useStyles = makeStyles(theme => ({ ...commonStyles(theme), @@ -21,14 +24,24 @@ export const OrgUnitChangeRequestConfigs: FunctionComponent = () => { const params = useParamsObject( baseUrls.orgUnitsChangeRequestConfiguration, ) as unknown as OrgUnitChangeRequestConfigsParams; + const { data, isFetching } = useGetOrgUnitChangeRequestConfigs(params); - const [isUpdateDialogOpen, setIsUpdateDialogOpen] = + const [isCreationSecondStepDialogOpen, setIsCreationSecondStepDialogOpen] = useState(false); + const [configInCreation, setConfigInCreation] = useState({}); + + const handleCreationSecondStep = useCallback( + (config) => { + setConfigInCreation(config); + setIsCreationSecondStepDialogOpen(true); + }, + [setIsCreationSecondStepDialogOpen, setConfigInCreation], + ); const classes: Record = useStyles(); const { formatMessage } = useSafeIntl(); - console.log("*** state isDialogOpen = ", isUpdateDialogOpen); + console.log("*** state isDialogOpen = ", isCreationSecondStepDialogOpen); return (
@@ -41,14 +54,17 @@ export const OrgUnitChangeRequestConfigs: FunctionComponent = () => { - { - console.log("*** openUpdateDialog ***"); - setIsUpdateDialogOpen(true); + openCreationSecondStepDialog={handleCreationSecondStep} + /> + { + setIsCreationSecondStepDialogOpen(false); }} + config={configInCreation} /> - { setIsUpdateDialogOpen(false) }} /> { - return deleteRequest(`${apiUrl}/${config.id}/`) as Promise; + return deleteRequest(`${apiUrlOUCRC}/${config.id}/`) as Promise; }; export const useDeleteOrgUnitChangeRequestConfig = (): UseMutationResult => diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useCheckAvailabilityOrgUnitChangeRequestConfigs.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetFormDropdownOptions.ts similarity index 51% rename from hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useCheckAvailabilityOrgUnitChangeRequestConfigs.ts rename to hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetFormDropdownOptions.ts index c18259d793..fcea5e9f8f 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useCheckAvailabilityOrgUnitChangeRequestConfigs.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetFormDropdownOptions.ts @@ -1,30 +1,31 @@ import { UseQueryResult } from 'react-query'; -import { getRequest } from '../../../../../libs/Api'; + import { useSnackQuery } from '../../../../../libs/apiHooks'; +import { getRequest } from '../../../../../libs/Api'; -import { apiUrl } from '../../constants'; -import { OrgUnitType } from '../../types'; +import { Form } from '../../../../forms/types/forms'; import { DropdownOptions } from '../../../../../types/utils'; +import { apiUrlForms } from '../../constants'; -export const useCheckAvailabilityOrgUnitChangeRequestConfigs = ( - projectId?: number, +export const useGetFormDropdownOptions = ( + orgUnitTypeId?: number, ): UseQueryResult[], Error> => { - const url = `${apiUrl}check_availability/?project_id=${projectId}`; + const url = `${apiUrlForms}?orgUnitTypeIds=${orgUnitTypeId}`; return useSnackQuery({ - // Including locale in the query key because we need to make a call to update translations coming from the backend - queryKey: ['checkAvailabilityOrgUnitChangeRequestConfigs', url], + queryKey: ['useGetFormDropdownOptions', url], queryFn: () => getRequest(url), options: { - enabled: Boolean(projectId), + enabled: Boolean(orgUnitTypeId), staleTime: 1000 * 60 * 15, // in MS cacheTime: 1000 * 60 * 5, keepPreviousData: true, + retry: false, select: data => { return ( - data?.results?.map((orgUnitType: OrgUnitType) => { + data?.forms?.map((form: Form) => { return { - value: orgUnitType.id, - label: orgUnitType.name, + value: form.id, + label: form.name, }; }) ?? [] ); diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOUCRCCheckAvailabilityDropdownOptions.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOUCRCCheckAvailabilityDropdownOptions.ts new file mode 100644 index 0000000000..b65b2913d5 --- /dev/null +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOUCRCCheckAvailabilityDropdownOptions.ts @@ -0,0 +1,46 @@ +import { UseQueryResult } from 'react-query'; +import { getRequest } from '../../../../../libs/Api'; +import { useSnackQuery } from '../../../../../libs/apiHooks'; + +import { apiUrlOUCRCCheckAvailability } from '../../constants'; +import { OrgUnitType } from '../../types'; +import { DropdownOptions } from '../../../../../types/utils'; + +export const useGetOUCRCCheckAvailabilityDropdownOptions = ( + projectId?: number, +): UseQueryResult[], Error> => { + const url = `${apiUrlOUCRCCheckAvailability}?project_id=${projectId}`; + return useSnackQuery({ + queryKey: ['checkAvailabilityOrgUnitChangeRequestConfigs', url], + queryFn: () => getRequest(url), + options: { + enabled: Boolean(projectId), + staleTime: 1000 * 60 * 15, // in MS + cacheTime: 1000 * 60 * 5, + keepPreviousData: true, + retry: false, + select: () => { + return [ + { + value: 1, + label: 'test1', + }, + { + value: 2, + label: 'test2', + }, + { + value: 3, + label: 'test3', + }, + ]; + // data?.results?.map((orgUnitType: OrgUnitType) => { + // value: orgUnitType.id, + // label: orgUnitType.name, + // }; + // }) ?? [] + // ); + }, + }, + }); +}; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOrgUnitChangeRequestConfigs.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOrgUnitChangeRequestConfigs.ts index 99ba4fd81f..c177a1e4bf 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOrgUnitChangeRequestConfigs.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOrgUnitChangeRequestConfigs.ts @@ -4,7 +4,7 @@ import { getRequest } from '../../../../../libs/Api'; import { useSnackQuery } from '../../../../../libs/apiHooks'; import { useLocale } from '../../../../app/contexts/LocaleContext'; -import { apiUrl } from '../../constants'; +import { apiUrlOUCRC } from '../../constants'; import { OrgUnitChangeRequestConfigsPaginated, OrgUnitChangeRequestConfigsParams, @@ -26,7 +26,7 @@ export const useGetOrgUnitChangeRequestConfigs = ( page: params.page, }; - const url = makeUrlWithParams(apiUrl, apiParams); + const url = makeUrlWithParams(apiUrlOUCRC, apiParams); return useSnackQuery({ // Including locale in the query key because we need to make a call to update translations coming from the backend queryKey: ['getOrgUnitChangeRequestConfigs', url, locale], diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts index 9f833d03de..049d66789d 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts @@ -73,10 +73,82 @@ const MESSAGES = defineMessages({ id: 'iaso.label.oucrcCreateModalTitle', defaultMessage: 'OrgUnit Change Request Configuration - Creation', }, + oucrcCreateSecondStepModalTitle: { + id: 'iaso.label.oucrcCreateModalTitle', + defaultMessage: 'OrgUnit Change Request Configuration - Creation 2nd step', + }, + oucrcCreateUpdateModalTitle: { + id: 'iaso.label.oucrcCreateModalTitle', + defaultMessage: 'OrgUnit Change Request Configuration - Update', + }, requiredField: { id: 'iaso.forms.error.fieldRequired', defaultMessage: 'This field is required', }, + orgUnitsEditable: { + id: 'iaso.label.orgUnitsEditable', + defaultMessage: 'Should OrgUnits of this type be editable?', + }, + orgUnitsEditableYes: { + id: 'iaso.label.orgUnitsEditableYes', + defaultMessage: 'Yes', + }, + orgUnitsEditableNo: { + id: 'iaso.label.orgUnitsEditableNo', + defaultMessage: 'No', + }, + name: { + id: 'iaso.label.name', + defaultMessage: 'Name', + }, + aliases: { + id: 'iaso.label.aliases', + defaultMessage: 'Aliases', + }, + openingDate: { + id: 'iaso.label.openingDate', + defaultMessage: 'Opening Date', + }, + closedDate: { + id: 'iaso.label.closedDate', + defaultMessage: 'Closing Date', + }, + location: { + id: 'iaso.label.location', + defaultMessage: 'Location', + }, + editableFields: { + id: 'iaso.label.editableFields', + defaultMessage: 'Editable Fields', + }, + possibleTypes: { + id: 'iaso.label.possibleTypes', + defaultMessage: 'Possible New Types', + }, + possibleParentTypes: { + id: 'iaso.label.possibleParentTypes', + defaultMessage: 'Possible New Parent Types', + }, + groupSets: { + id: 'iaso.label.groupSets', + defaultMessage: 'Group Sets', + }, + editableReferenceForms: { + id: 'iaso.label.editableReferenceForms', + defaultMessage: 'Editable Reference Forms', + }, + otherGroups: { + id: 'iaso.label.otherGroups', + defaultMessage: 'Other Groups', + }, + oucrcModalCreateButton: { + id: 'iaso.label.oucrcModalCreateButton', + defaultMessage: 'Create', + }, + oucrcModalUpdateButton: { + id: 'iaso.label.oucrcModalUpdateButton', + defaultMessage: 'Update', + }, }); export default MESSAGES; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts index 24c5531a5f..16267112fa 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts @@ -47,41 +47,15 @@ export type OrgUnitChangeRequestConfigListElement = { export type OrgUnitChangeRequestConfigurationFull = { id?: number; - project?: Project; - org_unit_type?: OrgUnitType; - org_units_editable?: boolean; - editable_fields?: Array; - possible_types?: Array; - possible_parent_types?: Array; - group_sets?: Array; - editable_reference_forms?: Array; - other_groups?: Array; - created_by?: NestedUser; - created_at?: number; - updated_by?: NestedUser; - updated_at?: number; -}; - -export type OrgUnitChangeRequestConfigurationCreate = { - project_id: number; - org_unit_type_id: number; - org_units_editable?: boolean; - editable_fields?: Array; - possible_type_ids?: Array; - possible_parent_type_ids?: Array; - group_set_ids?: Array; - editable_reference_form_ids?: Array; - other_group_ids?: Array; -}; - -export type OrgUnitChangeRequestConfigurationUpdate = { - org_units_editable?: boolean; - editable_fields?: Array; - possible_type_ids?: Array; - possible_parent_type_ids?: Array; - group_set_ids?: Array; - editable_reference_form_ids?: Array; - other_group_ids?: Array; + projectId?: number; + orgUnitTypeId?: number; + orgUnitsEditable?: boolean; + editableFields?: Array; + possibleTypeIds?: Array; + possibleParentTypeIds?: Array; + groupSetIds?: Array; + editableReferenceFormIds?: Array; + otherGroupIds?: Array; }; export interface OrgUnitChangeRequestConfigsPaginated extends Pagination { From 5461b3468470d6d094431c99fb6e5b79a82e74c0 Mon Sep 17 00:00:00 2001 From: Thibault Dethier Date: Fri, 20 Sep 2024 12:41:35 +0200 Subject: [PATCH 04/22] [IA-3443] OUCRC create/update - WIP --- ...angeRequestConfigDialogCreateFirstStep.tsx | 25 ++++-- ...OrgUnitChangeRequestConfigDialogUpdate.tsx | 81 ++++++++++++------- .../OrgUnitChangeRequestConfigs.tsx | 37 +++++---- .../OrgUnitChangeRequestConfigsTable.tsx | 12 ++- .../orgUnits/configuration/constants.ts | 17 ++-- ...seSaveOrgUnitChangeRequestConfiguration.ts | 30 +++++++ .../hooks/useOrgUnitsEditableOptions.ts | 17 ++++ .../orgUnits/configuration/messages.ts | 20 ++--- .../domains/orgUnits/configuration/types.ts | 22 +++-- 9 files changed, 182 insertions(+), 79 deletions(-) create mode 100644 hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts create mode 100644 hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useOrgUnitsEditableOptions.ts diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx index ff1676efc0..cd8fd3210b 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx @@ -33,10 +33,10 @@ const useCreationSchema = () => { }; const OrgUnitChangeRequestConfigDialogCreateFirstStep: FunctionComponent = ({ - isOpen, - closeDialog, - openCreationSecondStepDialog, -}) => { + isOpen, + closeDialog, + openCreationSecondStepDialog, + }) => { const creationSchema = useCreationSchema(); const { values, @@ -54,7 +54,22 @@ const OrgUnitChangeRequestConfigDialogCreateFirstStep: FunctionComponent }, validationSchema: creationSchema, onSubmit: () => { - openCreationSecondStepDialog(values); + const projectOption = allProjects?.find(project => + project.id === values.projectId, + ); + const orgUnitTypeOption = orgUnitTypeOptions?.find(orgUnitType => + orgUnitType.id === values.orgUnitTypeId, + ); + openCreationSecondStepDialog({ + project: projectOption ? { + id: projectOption.value, + name: projectOption.label, + } : undefined, + orgUnitType: orgUnitTypeOption ? { + id: orgUnitTypeOption.value, + name: orgUnitTypeOption.label, + } : undefined, + }); }, }); diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx index afeacd9523..36a4bf6aba 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx @@ -1,4 +1,4 @@ -import React, { FunctionComponent, useCallback, useState } from 'react'; +import React, { FunctionComponent, useCallback } from 'react'; import { ConfirmCancelModal, IntlFormatMessage, @@ -13,13 +13,16 @@ import { useTranslatedErrors } from '../../../../libs/validation'; import MESSAGES from '../messages'; import { EditIconButton } from '../../../../components/Buttons/EditIconButton'; import InputComponent from '../../../../components/forms/InputComponent'; -import { editableFields } from '../constants'; +import { editableFields, editableFieldsForFrontend } from '../constants'; import { useGetOrgUnitTypesDropdownOptions } from '../../orgUnitTypes/hooks/useGetOrgUnitTypesDropdownOptions'; import { useGetGroupDropdown } from '../../hooks/requests/useGetGroups'; import { useGetFormDropdownOptions } from '../hooks/api/useGetFormDropdownOptions'; +import { useSaveOrgUnitChangeRequestConfiguration } from '../hooks/api/useSaveOrgUnitChangeRequestConfiguration'; +import { useOrgUnitsEditableOptions } from '../hooks/useOrgUnitsEditableOptions'; +import { OrgUnitChangeRequestConfiguration } from '../types'; type Props = { - config: object; + config: OrgUnitChangeRequestConfiguration; isOpen: boolean; closeDialog: () => void; }; @@ -30,7 +33,7 @@ const useCreationSchema = () => { projectId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), // orgUnitTypeId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), orgUnitTypeId: Yup.string().nullable(), - orgUnitsEditable: Yup.boolean().nullable().required(formatMessage(MESSAGES.requiredField)), + orgUnitsEditable: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), editableFields: Yup.array().of(Yup.string()).nullable(), possibleTypeIds: Yup.array().of(Yup.string()).nullable(), possibleParentTypeIds: Yup.array().of(Yup.string()).nullable(), @@ -87,6 +90,8 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ const { data: groupOptions, isLoading: isLoadingGroups } = useGetGroupDropdown({}); const { data: formOptions, isFetching: isFetchingForms } = useGetFormDropdownOptions(); const { data: groupSetOptions, isLoading: isLoadingGroupSets } = useGetGroupDropdown({}); + const { mutateAsync: saveConfig } = useSaveOrgUnitChangeRequestConfiguration(); + const orgUnitsEditableOptions = useOrgUnitsEditableOptions(); const { formatMessage } = useSafeIntl(); const getErrors = useTranslatedErrors({ @@ -106,7 +111,30 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ [setFieldValue, setFieldTouched], ); - console.log('*** update - values = ', values); + const onChangeEditableFields = useCallback((keyValue, value) => { + const split = value.split(','); + editableFieldsForFrontend.forEach((field) => { + if (!split.includes(field)) { + setFieldValue(field, undefined); + } + }); + onChange(keyValue, value); + }, + [onChange, setFieldValue], + ); + + const onChangeOrgUnitsEditable = useCallback((keyValue, value) => { + const boolValue = value === 'true'; + if (!boolValue) { + editableFieldsForFrontend.forEach((field) => { + setFieldValue(field, undefined); + }); + setFieldValue('editableFields', undefined); + } + onChange(keyValue, boolValue); + }, + [onChange, setFieldValue], + ); const allowConfirm = isValid && !isSubmitting && !isEqual(touched, {}); @@ -122,15 +150,15 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ : formatMessage(MESSAGES.oucrcCreateSecondStepModalTitle) } closeDialog={closeDialog} - maxWidth="xs" + maxWidth="sm" allowConfirm={allowConfirm} cancelMessage={MESSAGES.cancel} confirmMessage={ config?.id ? MESSAGES.oucrcModalUpdateButton : MESSAGES.oucrcModalCreateButton - // ? formatMessage(MESSAGES.oucrcModalUpdateButton) - // : formatMessage(MESSAGES.oucrcModalCreateButton) + // ? formatMessage(MESSAGES.oucrcModalUpdateButton) + // : formatMessage(MESSAGES.oucrcModalCreateButton) } onConfirm={() => handleSubmit()} onCancel={() => { @@ -152,34 +180,25 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ - {values?.orgUnitsEditable === 'true' && ( + {values?.orgUnitsEditable && ( )} - {values?.editableFields && values.editableFields.includes("possibleTypes") && ( + {values?.editableFields && values.editableFields.includes('possibleTypeIds') && ( = ({ onChange={onChange} value={values.possibleTypeIds} errors={getErrors('possibleTypeIds')} - label={MESSAGES.possibleTypes} + label={MESSAGES.possibleTypeIds} options={orgUnitTypeOptions} /> )} - {values?.editableFields && values.editableFields.includes("possibleParentTypes") && ( + {values?.editableFields && values.editableFields.includes('possibleParentTypeIds') && ( = ({ onChange={onChange} value={values.possibleParentTypeIds} errors={getErrors('possibleParentTypeIds')} - label={MESSAGES.possibleParentTypes} + label={MESSAGES.possibleParentTypeIds} options={orgUnitTypeOptions} // Warning: we should probably filter data here (only what is available in parent/child relationship) /> )} - {values?.editableFields && values.editableFields.includes("groupSets") && ( + {values?.editableFields && values.editableFields.includes('groupSetIds') && ( = ({ onChange={onChange} value={values.groupSetIds} errors={getErrors('groupSetIds')} - label={MESSAGES.groupSets} + label={MESSAGES.groupSetIds} options={groupSetOptions} // Warning: no call for groupsets ATM (using groups as placeholder) /> )} - {values?.editableFields && values.editableFields.includes("editableReferenceForms") && ( + {values?.editableFields && values.editableFields.includes('editableReferenceFormIds') && ( = ({ onChange={onChange} value={values.editableReferenceFormIds} errors={getErrors('editableReferenceFormIds')} - label={MESSAGES.editableReferenceForms} + label={MESSAGES.editableReferenceFormIds} options={formOptions} /> )} - {values?.editableFields && values.editableFields.includes("otherGroups") && ( + {values?.editableFields && values.editableFields.includes('otherGroupIds') && ( = ({ onChange={onChange} value={values.otherGroupIds} errors={getErrors('otherGroupIds')} - label={MESSAGES.otherGroups} + label={MESSAGES.otherGroupIds} options={groupOptions} // Warning: we should probably filter data here (not in groupsets) /> )} diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx index 543952c8fa..c9560e2987 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx @@ -4,7 +4,10 @@ import React, { FunctionComponent, useCallback, useState } from 'react'; import { Box } from '@mui/material'; import { useParamsObject } from '../../../routing/hooks/useParamsObject'; import { baseUrls } from '../../../constants/urls'; -import { OrgUnitChangeRequestConfigsParams } from './types'; +import { + OrgUnitChangeRequestConfigsParams, + OrgUnitChangeRequestConfiguration, +} from './types'; import { useGetOrgUnitChangeRequestConfigs } from './hooks/api/useGetOrgUnitChangeRequestConfigs'; import TopBar from '../../../components/nav/TopBarComponent'; import MESSAGES from './messages'; @@ -28,14 +31,14 @@ export const OrgUnitChangeRequestConfigs: FunctionComponent = () => { const { data, isFetching } = useGetOrgUnitChangeRequestConfigs(params); const [isCreationSecondStepDialogOpen, setIsCreationSecondStepDialogOpen] = useState(false); - const [configInCreation, setConfigInCreation] = useState({}); + const [config, setConfig] = useState(); - const handleCreationSecondStep = useCallback( - (config) => { - setConfigInCreation(config); + const handleSecondStep = useCallback( + (newConfig) => { + setConfig(newConfig); setIsCreationSecondStepDialogOpen(true); }, - [setIsCreationSecondStepDialogOpen, setConfigInCreation], + [setIsCreationSecondStepDialogOpen, setConfig], ); const classes: Record = useStyles(); @@ -56,24 +59,24 @@ export const OrgUnitChangeRequestConfigs: FunctionComponent = () => { - { - setIsCreationSecondStepDialogOpen(false); - }} - config={configInCreation} + openCreationSecondStepDialog={handleSecondStep} /> + {isCreationSecondStepDialogOpen && ( + { + setIsCreationSecondStepDialogOpen(false); + }} + config={config} + /> + )} { - // TODO - }} + onEditClicked={handleSecondStep} /> diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/OrgUnitChangeRequestConfigsTable.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/OrgUnitChangeRequestConfigsTable.tsx index 4e48abb72b..ec59d9c0ee 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/OrgUnitChangeRequestConfigsTable.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/OrgUnitChangeRequestConfigsTable.tsx @@ -11,7 +11,7 @@ import { baseUrls } from '../../../../constants/urls'; import { OrgUnitChangeRequestConfigsPaginated, OrgUnitChangeRequestConfigsParams, - OrgUnitChangeRequestConfig, + OrgUnitChangeRequestConfiguration, } from '../types'; import MESSAGES from '../messages'; import { DateTimeCell } from '../../../../components/Cells/DateTimeCell'; @@ -19,7 +19,7 @@ import { EditIconButton } from '../../../../components/Buttons/EditIconButton'; import { ConfirmDeleteModal } from '../Dialog/ConfirmDeleteModal'; const useColumns = ( - onEditClicked: Dispatch>, + onEditClicked: Dispatch>, ): Column[] => { const { formatMessage } = useSafeIntl(); // @ts-ignore @@ -66,7 +66,13 @@ const useColumns = ( sortable: false, Cell: settings => { const handleEdit = useCallback(() => { - onEditClicked(settings.row.original); + const configToUpdate = { + id: settings.row.original.id, + project: settings.row.original.project, + orgUnitType: settings.row.original.org_unit_type, + }; + onEditClicked(configToUpdate); + // onEditClicked(settings.row.original); }, [settings.row.original]); return ( <> diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/constants.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/constants.ts index 2bc3b2a3f1..d6dcb2147a 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/constants.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/constants.ts @@ -7,11 +7,11 @@ export const editableFields = [ 'openingDate', 'closedDate', 'location', - 'possibleTypes', - 'possibleParentTypes', - 'groupSets', - 'editableReferenceForms', - 'otherGroups', + 'possibleTypeIds', + 'possibleParentTypeIds', + 'groupSetIds', + 'editableReferenceFormIds', + 'otherGroupIds', ]; // only these values are allowed by the backend, the others are there to make fields appear export const editableFieldsForBackend = [ @@ -21,3 +21,10 @@ export const editableFieldsForBackend = [ 'closedDate', 'location', ]; +export const editableFieldsForFrontend = [ + 'possibleTypeIds', + 'possibleParentTypeIds', + 'groupSetIds', + 'editableReferenceFormIds', + 'otherGroupIds', +]; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts new file mode 100644 index 0000000000..d620d75bcd --- /dev/null +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts @@ -0,0 +1,30 @@ +import { UseMutationResult } from 'react-query'; +import { patchRequest, postRequest } from '../../../../../libs/Api'; +import { useSnackMutation } from '../../../../../libs/apiHooks'; + +import { apiUrlOUCRC } from '../../constants'; +import { OrgUnitChangeRequestConfigurationFull } from '../../types'; + +const patchOrgUniType = async (body: Partial) => { + const url = `${apiUrlOUCRC}${body.id}/`; + return patchRequest(url, body); +}; + +const postOrgUnitType = async (body: OrgUnitChangeRequestConfigurationFull) => { + return postRequest({ + url: `${apiUrlOUCRC}`, + data: body, + }); +}; + +export const useSaveOrgUnitChangeRequestConfiguration = (): UseMutationResult => { + const ignoreErrorCodes = [400]; + return useSnackMutation({ + mutationFn: (data: Partial) => + data.id + ? patchOrgUniType(data) + : postOrgUnitType(data as OrgUnitChangeRequestConfigurationFull), + // invalidateQueryKey: ['paginated-orgunit-types'], + ignoreErrorCodes, + }); +}; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useOrgUnitsEditableOptions.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useOrgUnitsEditableOptions.ts new file mode 100644 index 0000000000..73ba060397 --- /dev/null +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useOrgUnitsEditableOptions.ts @@ -0,0 +1,17 @@ +import { useSafeIntl } from 'bluesquare-components'; +import { DropdownOptions } from '../../../../types/utils'; +import MESSAGES from '../messages'; + +export const useOrgUnitsEditableOptions = (): DropdownOptions[] => { + const { formatMessage } = useSafeIntl(); + return [ + { + label: formatMessage(MESSAGES.orgUnitsEditableYes), + value: true, + }, + { + label: formatMessage(MESSAGES.orgUnitsEditableNo), + value: false, + }, + ]; +}; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts index 049d66789d..142d009ea7 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts @@ -121,24 +121,24 @@ const MESSAGES = defineMessages({ id: 'iaso.label.editableFields', defaultMessage: 'Editable Fields', }, - possibleTypes: { - id: 'iaso.label.possibleTypes', + possibleTypeIds: { + id: 'iaso.label.possibleTypeIds', defaultMessage: 'Possible New Types', }, - possibleParentTypes: { - id: 'iaso.label.possibleParentTypes', + possibleParentTypeIds: { + id: 'iaso.label.possibleParentTypeIds', defaultMessage: 'Possible New Parent Types', }, - groupSets: { - id: 'iaso.label.groupSets', + groupSetIds: { + id: 'iaso.label.groupSetIds', defaultMessage: 'Group Sets', }, - editableReferenceForms: { - id: 'iaso.label.editableReferenceForms', + editableReferenceFormIds: { + id: 'iaso.label.editableReferenceFormIds', defaultMessage: 'Editable Reference Forms', }, - otherGroups: { - id: 'iaso.label.otherGroups', + otherGroupIds: { + id: 'iaso.label.otherGroupIds', defaultMessage: 'Other Groups', }, oucrcModalCreateButton: { diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts index 16267112fa..9628ea2eee 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts @@ -46,16 +46,22 @@ export type OrgUnitChangeRequestConfigListElement = { }; export type OrgUnitChangeRequestConfigurationFull = { - id?: number; - projectId?: number; - orgUnitTypeId?: number; + id: number; + project: Project; + orgUnitType: OrgUnitType; orgUnitsEditable?: boolean; editableFields?: Array; - possibleTypeIds?: Array; - possibleParentTypeIds?: Array; - groupSetIds?: Array; - editableReferenceFormIds?: Array; - otherGroupIds?: Array; + possibleTypes?: Array; + possibleParentTypes?: Array; + groupSets?: Array; + editableReferenceForms?: Array; + otherGroups?: Array; +}; + +export type OrgUnitChangeRequestConfiguration = { + id?: number; + project: Project; + orgUnitType: OrgUnitType; }; export interface OrgUnitChangeRequestConfigsPaginated extends Pagination { From f7c1a984d688e684cb376e2d7bb4a63f49b65882 Mon Sep 17 00:00:00 2001 From: Thibault Dethier Date: Fri, 20 Sep 2024 17:37:45 +0200 Subject: [PATCH 05/22] [IA-3443] OUCRC create v1.0 --- ...angeRequestConfigDialogCreateFirstStep.tsx | 20 +- ...OrgUnitChangeRequestConfigDialogUpdate.tsx | 220 ++++++++---------- .../OrgUnitChangeRequestConfigs.tsx | 5 +- .../OrgUnitChangeRequestConfigsTable.tsx | 1 - .../orgUnits/configuration/constants.ts | 2 +- .../hooks/api/useGetFormDropdownOptions.ts | 2 +- ...etOUCRCCheckAvailabilityDropdownOptions.ts | 26 +-- .../api/useGetOrgUnitChangeRequestConfigs.ts | 1 - .../useRetrieveOrgUnitChangeRequestConfig.ts | 26 +++ ...seSaveOrgUnitChangeRequestConfiguration.ts | 65 +++++- .../hooks/useOrgUnitEditableFieldsOptions.ts | 14 ++ .../hooks/useValidationSchemaOUCRC.ts | 69 ++++++ 12 files changed, 286 insertions(+), 165 deletions(-) create mode 100644 hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts create mode 100644 hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useOrgUnitEditableFieldsOptions.ts create mode 100644 hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx index cd8fd3210b..88765c0e22 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx @@ -7,6 +7,7 @@ import { } from 'bluesquare-components'; import * as Yup from 'yup'; import { useFormik } from 'formik'; +import { isEqual } from 'lodash'; import { useTranslatedErrors } from '../../../../libs/validation'; import MESSAGES from '../messages'; import { useGetProjectsDropdownOptions } from '../../../projects/hooks/requests'; @@ -14,7 +15,6 @@ import InputComponent from '../../../../components/forms/InputComponent'; import { useGetOUCRCCheckAvailabilityDropdownOptions, } from '../hooks/api/useGetOUCRCCheckAvailabilityDropdownOptions'; -import { isEqual } from 'lodash'; type Props = { isOpen: boolean; @@ -27,16 +27,15 @@ const useCreationSchema = () => { const { formatMessage } = useSafeIntl(); return Yup.object().shape({ projectId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), - // orgUnitTypeId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), - orgUnitTypeId: Yup.string().nullable(), + orgUnitTypeId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), }); }; const OrgUnitChangeRequestConfigDialogCreateFirstStep: FunctionComponent = ({ - isOpen, - closeDialog, - openCreationSecondStepDialog, - }) => { +isOpen, +closeDialog, +openCreationSecondStepDialog, +}) => { const creationSchema = useCreationSchema(); const { values, @@ -55,10 +54,10 @@ const OrgUnitChangeRequestConfigDialogCreateFirstStep: FunctionComponent validationSchema: creationSchema, onSubmit: () => { const projectOption = allProjects?.find(project => - project.id === values.projectId, + `${project.value}` === `${values.projectId}`, ); const orgUnitTypeOption = orgUnitTypeOptions?.find(orgUnitType => - orgUnitType.id === values.orgUnitTypeId, + `${orgUnitType.value}` === `${values.orgUnitTypeId}`, ); openCreationSecondStepDialog({ project: projectOption ? { @@ -95,8 +94,7 @@ const OrgUnitChangeRequestConfigDialogCreateFirstStep: FunctionComponent [setFieldValue, setFieldTouched], ); - // const allowConfirm = isValid && !isSubmitting && !isEqual(touched, {}); - const allowConfirm = true; + const allowConfirm = isValid && !isSubmitting && !isEqual(touched, {}); return ( void; }; -const useCreationSchema = () => { - const { formatMessage } = useSafeIntl(); - return Yup.object().shape({ - projectId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), - // orgUnitTypeId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), - orgUnitTypeId: Yup.string().nullable(), - orgUnitsEditable: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), - editableFields: Yup.array().of(Yup.string()).nullable(), - possibleTypeIds: Yup.array().of(Yup.string()).nullable(), - possibleParentTypeIds: Yup.array().of(Yup.string()).nullable(), - groupSetIds: Yup.array().of(Yup.string()).nullable(), - editableReferenceFormIds: Yup.array().of(Yup.string()).nullable(), - otherGroupIds: Yup.array().of(Yup.string()).nullable(), - }); -}; - -const editableFieldsOptions = (formatMessage: IntlFormatMessage) => { - return editableFields.map(field => { - return { - value: field, - label: formatMessage(MESSAGES[field]), - }; - }); -}; - const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ config, isOpen, closeDialog, }) => { - const creationSchema = useCreationSchema(); + const { data: fullConfig, isLoading: isLoadingFullConfig } = useRetrieveOrgUnitChangeRequestConfig(config?.id); + const configValidationSchema = useValidationSchemaOUCRC(); const { values, setFieldValue, @@ -69,10 +47,9 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ setFieldTouched, } = useFormik({ initialValues: { - projectId: config.projectId, - // orgUnitTypeId: config.orgUnitTypeId, - orgUnitTypeId: 6, - orgUnitsEditable: '', + projectId: config.project.id, + orgUnitTypeId: config.orgUnitType.id, + orgUnitsEditable: undefined, editableFields: undefined, possibleTypeIds: undefined, possibleParentTypeIds: undefined, @@ -80,18 +57,21 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ editableReferenceFormIds: undefined, otherGroupIds: undefined, }, - validationSchema: creationSchema, + validationSchema: configValidationSchema, onSubmit: () => { console.log('*** onSubmit values = ', values); + saveConfig(values); + closeDialog(); }, }); - const { data: orgUnitTypeOptions, isLoading: isLoadingTypes } = useGetOrgUnitTypesDropdownOptions(); - const { data: groupOptions, isLoading: isLoadingGroups } = useGetGroupDropdown({}); - const { data: formOptions, isFetching: isFetchingForms } = useGetFormDropdownOptions(); - const { data: groupSetOptions, isLoading: isLoadingGroupSets } = useGetGroupDropdown({}); + const { data: orgUnitTypeOptions } = useGetOrgUnitTypesDropdownOptions(); + const { data: groupOptions } = useGetGroupDropdown({}); + const { data: formOptions } = useGetFormDropdownOptions(config.orgUnitType.id); + const { data: groupSetOptions } = useGetGroupDropdown({}); const { mutateAsync: saveConfig } = useSaveOrgUnitChangeRequestConfiguration(); const orgUnitsEditableOptions = useOrgUnitsEditableOptions(); + const orgUnitEditableFieldsOptions = useOrgUnitsEditableFieldsOptions(); const { formatMessage } = useSafeIntl(); const getErrors = useTranslatedErrors({ @@ -103,8 +83,6 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ const onChange = useCallback( (keyValue, value) => { - console.log('*** update - onChange - keyValue = ', keyValue); - console.log('*** update - onChange - value = ', value); setFieldTouched(keyValue, true); setFieldValue(keyValue, value); }, @@ -112,21 +90,25 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ ); const onChangeEditableFields = useCallback((keyValue, value) => { - const split = value.split(','); - editableFieldsForFrontend.forEach((field) => { - if (!split.includes(field)) { - setFieldValue(field, undefined); - } - }); + // if a many-to-many field has some value, but the field is removed from editableFields, we need to clean the field + if (value) { + const split = value.split(','); + editableFieldsManyToManyFields.forEach((field) => { + if (!split.includes(field)) { + setFieldValue(field, undefined); + } + }); + } onChange(keyValue, value); }, [onChange, setFieldValue], ); const onChangeOrgUnitsEditable = useCallback((keyValue, value) => { + // if we say that the org units are no longer editable, we need to clean everything up const boolValue = value === 'true'; if (!boolValue) { - editableFieldsForFrontend.forEach((field) => { + editableFieldsManyToManyFields.forEach((field) => { setFieldValue(field, undefined); }); setFieldValue('editableFields', undefined); @@ -157,25 +139,18 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ config?.id ? MESSAGES.oucrcModalUpdateButton : MESSAGES.oucrcModalCreateButton - // ? formatMessage(MESSAGES.oucrcModalUpdateButton) - // : formatMessage(MESSAGES.oucrcModalCreateButton) } onConfirm={() => handleSubmit()} onCancel={() => { closeDialog(); }} > - - Project: {config.projectId} + {isLoadingFullConfig && } + + Project: {config.project.name} - - Org Unit Type: {config.orgUnitTypeId} + + Org Unit Type: {config.orgUnitType.name} = ({ value={values.editableFields} errors={getErrors('editableFields')} label={MESSAGES.editableFields} - options={editableFieldsOptions(formatMessage)} - /> - )} - {values?.editableFields && values.editableFields.includes('possibleTypeIds') && ( - - )} - {values?.editableFields && values.editableFields.includes('possibleParentTypeIds') && ( - - )} - {values?.editableFields && values.editableFields.includes('groupSetIds') && ( - - )} - {values?.editableFields && values.editableFields.includes('editableReferenceFormIds') && ( - - )} - {values?.editableFields && values.editableFields.includes('otherGroupIds') && ( - )} + {values?.editableFields && + values.editableFields.includes('possibleTypeIds') && ( + + )} + {values?.editableFields && + values.editableFields.includes('possibleParentTypeIds') && ( + + )} + {values?.editableFields && + values.editableFields.includes('groupSetIds') && ( + + )} + {values?.editableFields && + values.editableFields.includes('editableReferenceFormIds') && ( + + )} + {values?.editableFields && + values.editableFields.includes('otherGroupIds') && ( + + )} ); }; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx index c9560e2987..89259e2a9b 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx @@ -16,7 +16,6 @@ import { OrgUnitChangeRequestConfigsTable } from './Tables/OrgUnitChangeRequestC import { OrgUnitChangeRequestConfigDialogCreateFirstStep } from './Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep'; import { OrgUnitChangeRequestConfigDialogCreateSecondStep, - OrgUnitChangeRequestConfigDialogUpdate, } from './Dialog/OrgUnitChangeRequestConfigDialogUpdate'; const useStyles = makeStyles(theme => ({ @@ -44,8 +43,6 @@ export const OrgUnitChangeRequestConfigs: FunctionComponent = () => { const classes: Record = useStyles(); const { formatMessage } = useSafeIntl(); - console.log("*** state isDialogOpen = ", isCreationSecondStepDialogOpen); - return (
{ iconProps={{}} openCreationSecondStepDialog={handleSecondStep} /> - {isCreationSecondStepDialogOpen && ( + {isCreationSecondStepDialogOpen && config && ( { diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/OrgUnitChangeRequestConfigsTable.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/OrgUnitChangeRequestConfigsTable.tsx index ec59d9c0ee..85f9c49e12 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/OrgUnitChangeRequestConfigsTable.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/OrgUnitChangeRequestConfigsTable.tsx @@ -56,7 +56,6 @@ const useColumns = ( { Header: formatMessage(MESSAGES.editable_fields), id: 'editable_fields', - accessor: row => row.editable_fields.join(', '), width: 600, }, { diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/constants.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/constants.ts index d6dcb2147a..3d9c4b6773 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/constants.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/constants.ts @@ -21,7 +21,7 @@ export const editableFieldsForBackend = [ 'closedDate', 'location', ]; -export const editableFieldsForFrontend = [ +export const editableFieldsManyToManyFields = [ 'possibleTypeIds', 'possibleParentTypeIds', 'groupSetIds', diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetFormDropdownOptions.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetFormDropdownOptions.ts index fcea5e9f8f..72e56438d3 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetFormDropdownOptions.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetFormDropdownOptions.ts @@ -8,7 +8,7 @@ import { DropdownOptions } from '../../../../../types/utils'; import { apiUrlForms } from '../../constants'; export const useGetFormDropdownOptions = ( - orgUnitTypeId?: number, + orgUnitTypeId: number, ): UseQueryResult[], Error> => { const url = `${apiUrlForms}?orgUnitTypeIds=${orgUnitTypeId}`; return useSnackQuery({ diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOUCRCCheckAvailabilityDropdownOptions.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOUCRCCheckAvailabilityDropdownOptions.ts index b65b2913d5..e30765ce2e 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOUCRCCheckAvailabilityDropdownOptions.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOUCRCCheckAvailabilityDropdownOptions.ts @@ -19,27 +19,11 @@ export const useGetOUCRCCheckAvailabilityDropdownOptions = ( cacheTime: 1000 * 60 * 5, keepPreviousData: true, retry: false, - select: () => { - return [ - { - value: 1, - label: 'test1', - }, - { - value: 2, - label: 'test2', - }, - { - value: 3, - label: 'test3', - }, - ]; - // data?.results?.map((orgUnitType: OrgUnitType) => { - // value: orgUnitType.id, - // label: orgUnitType.name, - // }; - // }) ?? [] - // ); + select: (data) => { + return data?.map((orgUnitType: OrgUnitType) => ({ + value: orgUnitType.id, + label: orgUnitType.name, + })) ?? [] }, }, }); diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOrgUnitChangeRequestConfigs.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOrgUnitChangeRequestConfigs.ts index c177a1e4bf..5cea58eff7 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOrgUnitChangeRequestConfigs.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOrgUnitChangeRequestConfigs.ts @@ -35,7 +35,6 @@ export const useGetOrgUnitChangeRequestConfigs = ( staleTime: 1000 * 60 * 15, // in MS cacheTime: 1000 * 60 * 5, keepPreviousData: true, - retry: false, }, }); }; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts new file mode 100644 index 0000000000..2ad9b7a817 --- /dev/null +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts @@ -0,0 +1,26 @@ +import { UseQueryResult } from 'react-query'; +import { getRequest } from '../../../../../libs/Api'; +import { useSnackQuery } from '../../../../../libs/apiHooks'; + +import { apiUrlOUCRC } from '../../constants'; +import { OrgUnitChangeRequestConfigurationFull } from '../../types'; + +const retrieveOrgUnitChangeRequestConfig = (url: string) => { + return getRequest(url) as Promise; +}; + +export const useRetrieveOrgUnitChangeRequestConfig = ( + configId?: number, +): UseQueryResult => { + const url = `${apiUrlOUCRC}${configId}`; + return useSnackQuery({ + queryKey: ['useRetrieveOrgUnitChangeRequestConfig', url], + queryFn: () => retrieveOrgUnitChangeRequestConfig(url), + options: { + enabled: Boolean(configId), + staleTime: 1000 * 60 * 15, // in MS + cacheTime: 1000 * 60 * 5, + keepPreviousData: true, + }, + }); +}; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts index d620d75bcd..27e9c86136 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts @@ -2,9 +2,62 @@ import { UseMutationResult } from 'react-query'; import { patchRequest, postRequest } from '../../../../../libs/Api'; import { useSnackMutation } from '../../../../../libs/apiHooks'; -import { apiUrlOUCRC } from '../../constants'; +import { apiUrlOUCRC, editableFieldsForBackend } from '../../constants'; import { OrgUnitChangeRequestConfigurationFull } from '../../types'; +function cleanEditableFieldsForSaving(editableFields) { + if (!editableFields) { + return editableFields; + } + let cleanEditableFields = ''; + const splitFields = editableFields.split(','); + for (const field in splitFields) { + if (editableFieldsForBackend.includes(field)) { + cleanEditableFields += `${field},`; + } + } + if (cleanEditableFields.length) { + cleanEditableFields = cleanEditableFields.slice(0, -1); + } + return cleanEditableFields; +} + +function mapValuesForSaving(values) { + const apiValues = { + org_units_editable: values.orgUnitsEditable, + }; + + // These two fields can't be updated so they are only set for creation + if (!values.id) { + apiValues.project_id = values.projectId; + apiValues.org_unit_type_id = values.orgUnitTypeId; + } + + // This field must be cleaned because the backend accepts only some values + const cleanedEditableFields = cleanEditableFieldsForSaving(values.editableFields); + if (cleanedEditableFields) { + apiValues.editable_fields = cleanedEditableFields; + } + + // All the many to many fields are added if they have a value + if (values.possibleTypeIds) { + apiValues.possible_type_ids = values.possibleTypeIds.split(",").map(Number); + } + if (values.possibleParentTypeIds) { + apiValues.possible_parent_type_ids = values.possibleParentTypeIds.split(",").map(Number); + } + if (values.groupSetIds) { + apiValues.group_set_ids = values.groupSetIds.split(",").map(Number); + } + if (values.editableReferenceFormIds) { + apiValues.editable_reference_form_ids = values.editableReferenceFormIds.split(",").map(Number); + } + if (values.otherGroupIds) { + apiValues.other_group_ids = values.otherGroupIds.split(",").map(Number); + } + return apiValues; +}; + const patchOrgUniType = async (body: Partial) => { const url = `${apiUrlOUCRC}${body.id}/`; return patchRequest(url, body); @@ -20,10 +73,12 @@ const postOrgUnitType = async (body: OrgUnitChangeRequestConfigurationFull) => { export const useSaveOrgUnitChangeRequestConfiguration = (): UseMutationResult => { const ignoreErrorCodes = [400]; return useSnackMutation({ - mutationFn: (data: Partial) => - data.id - ? patchOrgUniType(data) - : postOrgUnitType(data as OrgUnitChangeRequestConfigurationFull), + mutationFn: (data: Partial) => { + const formattedData = mapValuesForSaving(data); + return formattedData.id + ? patchOrgUniType(formattedData) + : postOrgUnitType(formattedData as OrgUnitChangeRequestConfigurationFull); + }, // invalidateQueryKey: ['paginated-orgunit-types'], ignoreErrorCodes, }); diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useOrgUnitEditableFieldsOptions.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useOrgUnitEditableFieldsOptions.ts new file mode 100644 index 0000000000..b5b97453af --- /dev/null +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useOrgUnitEditableFieldsOptions.ts @@ -0,0 +1,14 @@ +import { useSafeIntl } from 'bluesquare-components'; +import { editableFields } from '../constants'; +import MESSAGES from '../messages'; +import { DropdownOptions } from '../../../../types/utils'; + +export const useOrgUnitsEditableFieldsOptions = (): DropdownOptions[] => { + const { formatMessage } = useSafeIntl(); + return editableFields.map(field => { + return { + value: field, + label: formatMessage(MESSAGES[field]), + }; + }); +}; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts new file mode 100644 index 0000000000..f8b0cb33f2 --- /dev/null +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts @@ -0,0 +1,69 @@ +import * as yup from 'yup'; +import { useSafeIntl } from 'bluesquare-components'; +import MESSAGES from '../messages'; + +yup.addMethod( + yup.string, + 'isMultiSelectValid', + function isMultiSelectValid(formatMessage) { + return this.test('isMultiSelectValid', '', (value, context) => { + const { path, createError, parent } = context; + if (!parent.editableFields) { + return true; + } + const splitFields = parent.editableFields.split(','); + const isFieldPopulated = parent[path]; + if (!isFieldPopulated && splitFields.includes(path)) { + return createError({ + path, + message: formatMessage(MESSAGES.requiredField), + }); + } + return true; + }); + }, +); + +export const useValidationSchemaOUCRC = () => { + const { formatMessage } = useSafeIntl(); + return yup.object().shape({ + projectId: yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), + orgUnitTypeId: yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), + orgUnitsEditable: yup.boolean().nullable().required(formatMessage(MESSAGES.requiredField)), + editableFields: yup.string().nullable().when('orgUnitsEditable', { + is: true, + then: yup.string().nullable().required(), + otherwise: yup.string().nullable(), + }), + possibleTypeIds: yup.string().nullable().when('orgUnitsEditable', { + is: true, + // @ts-ignore + then: yup.string().nullable().isMultiSelectValid(formatMessage), + otherwise: yup.string().nullable(), + }), + possibleParentTypeIds: yup.string().nullable().when('orgUnitsEditable', { + is: true, + // @ts-ignore + then: yup.string().nullable().isMultiSelectValid(formatMessage), + otherwise: yup.string().nullable(), + }), + groupSetIds: yup.string().nullable().when('orgUnitsEditable', { + is: true, + // @ts-ignore + then: yup.string().nullable().isMultiSelectValid(formatMessage), + otherwise: yup.string().nullable(), + }), + editableReferenceFormIds: yup.string().nullable().when('orgUnitsEditable', { + is: true, + // @ts-ignore + then: yup.string().nullable().isMultiSelectValid(formatMessage), + otherwise: yup.string().nullable(), + }), + otherGroupIds: yup.string().nullable().when('orgUnitsEditable', { + is: true, + // @ts-ignore + then: yup.string().nullable().isMultiSelectValid(formatMessage), + otherwise: yup.string().nullable(), + }), + }); +}; \ No newline at end of file From f1c963000609ddea29ee8c9195088ea84430374d Mon Sep 17 00:00:00 2001 From: Thibault Dethier Date: Wed, 25 Sep 2024 15:35:58 +0200 Subject: [PATCH 06/22] [IA-3443] OUCRC create/update - WIP --- ...angeRequestConfigDialogCreateFirstStep.tsx | 58 ++++++------ ...OrgUnitChangeRequestConfigDialogUpdate.tsx | 33 ++++--- .../OrgUnitChangeRequestConfigs.tsx | 7 +- .../OrgUnitChangeRequestConfigsTable.tsx | 3 +- .../hooks/api/useGetFormDropdownOptions.ts | 2 +- ...etOUCRCCheckAvailabilityDropdownOptions.ts | 6 +- .../useRetrieveOrgUnitChangeRequestConfig.ts | 40 +++++++- ...seSaveOrgUnitChangeRequestConfiguration.ts | 70 ++++++++------ .../hooks/useOrgUnitEditableFieldsOptions.ts | 19 ++-- .../hooks/useValidationSchemaOUCRC.ts | 92 ++++++++++++------- .../orgUnits/configuration/messages.ts | 3 +- .../domains/orgUnits/configuration/types.ts | 26 ++++-- 12 files changed, 230 insertions(+), 129 deletions(-) diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx index 88765c0e22..c2fb30f8e5 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx @@ -12,9 +12,7 @@ import { useTranslatedErrors } from '../../../../libs/validation'; import MESSAGES from '../messages'; import { useGetProjectsDropdownOptions } from '../../../projects/hooks/requests'; import InputComponent from '../../../../components/forms/InputComponent'; -import { - useGetOUCRCCheckAvailabilityDropdownOptions, -} from '../hooks/api/useGetOUCRCCheckAvailabilityDropdownOptions'; +import { useGetOUCRCCheckAvailabilityDropdownOptions } from '../hooks/api/useGetOUCRCCheckAvailabilityDropdownOptions'; type Props = { isOpen: boolean; @@ -26,16 +24,18 @@ type Props = { const useCreationSchema = () => { const { formatMessage } = useSafeIntl(); return Yup.object().shape({ - projectId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), - orgUnitTypeId: Yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), + projectId: Yup.string() + .nullable() + .required(formatMessage(MESSAGES.requiredField)), + orgUnitTypeId: Yup.string() + .nullable() + .required(formatMessage(MESSAGES.requiredField)), }); }; -const OrgUnitChangeRequestConfigDialogCreateFirstStep: FunctionComponent = ({ -isOpen, -closeDialog, -openCreationSecondStepDialog, -}) => { +const OrgUnitChangeRequestConfigDialogCreateFirstStep: FunctionComponent< + Props +> = ({ isOpen, closeDialog, openCreationSecondStepDialog }) => { const creationSchema = useCreationSchema(); const { values, @@ -53,21 +53,26 @@ openCreationSecondStepDialog, }, validationSchema: creationSchema, onSubmit: () => { - const projectOption = allProjects?.find(project => - `${project.value}` === `${values.projectId}`, + const projectOption = allProjects?.find( + project => `${project.value}` === `${values.projectId}`, ); - const orgUnitTypeOption = orgUnitTypeOptions?.find(orgUnitType => - `${orgUnitType.value}` === `${values.orgUnitTypeId}`, + const orgUnitTypeOption = orgUnitTypeOptions?.find( + orgUnitType => + `${orgUnitType.value}` === `${values.orgUnitTypeId}`, ); openCreationSecondStepDialog({ - project: projectOption ? { - id: projectOption.value, - name: projectOption.label, - } : undefined, - orgUnitType: orgUnitTypeOption ? { - id: orgUnitTypeOption.value, - name: orgUnitTypeOption.label, - } : undefined, + project: projectOption + ? { + id: projectOption.value, + name: projectOption.label, + } + : undefined, + orgUnitType: orgUnitTypeOption + ? { + id: orgUnitTypeOption.value, + name: orgUnitTypeOption.label, + } + : undefined, }); }, }); @@ -80,11 +85,10 @@ openCreationSecondStepDialog, messages: MESSAGES, }); - const { data: allProjects, isFetching: isFetchingProjects } = useGetProjectsDropdownOptions(); - const { - data: orgUnitTypeOptions, - isFetching: isFetchingOrgUnitTypes, - } = useGetOUCRCCheckAvailabilityDropdownOptions(values.projectId); + const { data: allProjects, isFetching: isFetchingProjects } = + useGetProjectsDropdownOptions(); + const { data: orgUnitTypeOptions, isFetching: isFetchingOrgUnitTypes } = + useGetOUCRCCheckAvailabilityDropdownOptions(values.projectId); const onChange = useCallback( (keyValue, value) => { diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx index fe2ed00b5b..894bab4316 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx @@ -19,7 +19,7 @@ import { useGetFormDropdownOptions } from '../hooks/api/useGetFormDropdownOption import { useSaveOrgUnitChangeRequestConfiguration } from '../hooks/api/useSaveOrgUnitChangeRequestConfiguration'; import { useOrgUnitsEditableOptions } from '../hooks/useOrgUnitsEditableOptions'; import { useOrgUnitsEditableFieldsOptions } from '../hooks/useOrgUnitEditableFieldsOptions'; -import { OrgUnitChangeRequestConfiguration } from '../types'; +import { OrgUnitChangeRequestConfiguration, OrgUnitChangeRequestConfigurationForm } from '../types'; import { useRetrieveOrgUnitChangeRequestConfig } from '../hooks/api/useRetrieveOrgUnitChangeRequestConfig'; import { useValidationSchemaOUCRC } from '../hooks/useValidationSchemaOUCRC'; @@ -34,7 +34,6 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ isOpen, closeDialog, }) => { - const { data: fullConfig, isLoading: isLoadingFullConfig } = useRetrieveOrgUnitChangeRequestConfig(config?.id); const configValidationSchema = useValidationSchemaOUCRC(); const { values, @@ -45,7 +44,8 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ errors, touched, setFieldTouched, - } = useFormik({ + resetForm, + } = useFormik({ initialValues: { projectId: config.project.id, orgUnitTypeId: config.orgUnitType.id, @@ -59,17 +59,26 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ }, validationSchema: configValidationSchema, onSubmit: () => { - console.log('*** onSubmit values = ', values); - saveConfig(values); + saveConfig(config.id, values); closeDialog(); }, }); + const { isLoading: isLoadingFullConfig } = + useRetrieveOrgUnitChangeRequestConfig(config?.id, fetchedConfig => { + console.log("*** before resetting form - fetchedConfig ", fetchedConfig); + resetForm(fetchedConfig); + }); + + console.log("*** values = ", values); const { data: orgUnitTypeOptions } = useGetOrgUnitTypesDropdownOptions(); const { data: groupOptions } = useGetGroupDropdown({}); - const { data: formOptions } = useGetFormDropdownOptions(config.orgUnitType.id); + const { data: formOptions } = useGetFormDropdownOptions( + config.orgUnitType.id, + ); const { data: groupSetOptions } = useGetGroupDropdown({}); - const { mutateAsync: saveConfig } = useSaveOrgUnitChangeRequestConfiguration(); + const { mutateAsync: saveConfig } = + useSaveOrgUnitChangeRequestConfiguration(); const orgUnitsEditableOptions = useOrgUnitsEditableOptions(); const orgUnitEditableFieldsOptions = useOrgUnitsEditableFieldsOptions(); @@ -89,11 +98,12 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ [setFieldValue, setFieldTouched], ); - const onChangeEditableFields = useCallback((keyValue, value) => { + const onChangeEditableFields = useCallback( + (keyValue, value) => { // if a many-to-many field has some value, but the field is removed from editableFields, we need to clean the field if (value) { const split = value.split(','); - editableFieldsManyToManyFields.forEach((field) => { + editableFieldsManyToManyFields.forEach(field => { if (!split.includes(field)) { setFieldValue(field, undefined); } @@ -104,11 +114,12 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ [onChange, setFieldValue], ); - const onChangeOrgUnitsEditable = useCallback((keyValue, value) => { + const onChangeOrgUnitsEditable = useCallback( + (keyValue, value) => { // if we say that the org units are no longer editable, we need to clean everything up const boolValue = value === 'true'; if (!boolValue) { - editableFieldsManyToManyFields.forEach((field) => { + editableFieldsManyToManyFields.forEach(field => { setFieldValue(field, undefined); }); setFieldValue('editableFields', undefined); diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx index 89259e2a9b..04c2c0da23 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx @@ -14,9 +14,7 @@ import MESSAGES from './messages'; import { OrgUnitChangeRequestConfigsFilter } from './Filter/OrgUnitChangeRequestConfigsFilter'; import { OrgUnitChangeRequestConfigsTable } from './Tables/OrgUnitChangeRequestConfigsTable'; import { OrgUnitChangeRequestConfigDialogCreateFirstStep } from './Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep'; -import { - OrgUnitChangeRequestConfigDialogCreateSecondStep, -} from './Dialog/OrgUnitChangeRequestConfigDialogUpdate'; +import { OrgUnitChangeRequestConfigDialogCreateSecondStep } from './Dialog/OrgUnitChangeRequestConfigDialogUpdate'; const useStyles = makeStyles(theme => ({ ...commonStyles(theme), @@ -33,7 +31,7 @@ export const OrgUnitChangeRequestConfigs: FunctionComponent = () => { const [config, setConfig] = useState(); const handleSecondStep = useCallback( - (newConfig) => { + newConfig => { setConfig(newConfig); setIsCreationSecondStepDialogOpen(true); }, @@ -53,7 +51,6 @@ export const OrgUnitChangeRequestConfigs: FunctionComponent = () => { - @@ -92,7 +91,7 @@ const useColumns = ( type Props = { data: OrgUnitChangeRequestConfigsPaginated | undefined; isFetching: boolean; - onEditClicked: Dispatch>; + onEditClicked: Dispatch>; params: OrgUnitChangeRequestConfigsParams; }; export const baseUrl = baseUrls.orgUnitsChangeRequestConfiguration; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetFormDropdownOptions.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetFormDropdownOptions.ts index 72e56438d3..989f84cbd5 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetFormDropdownOptions.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetFormDropdownOptions.ts @@ -10,7 +10,7 @@ import { apiUrlForms } from '../../constants'; export const useGetFormDropdownOptions = ( orgUnitTypeId: number, ): UseQueryResult[], Error> => { - const url = `${apiUrlForms}?orgUnitTypeIds=${orgUnitTypeId}`; + const url = `${apiUrlForms}?orgUnitTypeIds=${orgUnitTypeId}`; return useSnackQuery({ queryKey: ['useGetFormDropdownOptions', url], queryFn: () => getRequest(url), diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOUCRCCheckAvailabilityDropdownOptions.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOUCRCCheckAvailabilityDropdownOptions.ts index e30765ce2e..64e62d32c0 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOUCRCCheckAvailabilityDropdownOptions.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useGetOUCRCCheckAvailabilityDropdownOptions.ts @@ -19,11 +19,13 @@ export const useGetOUCRCCheckAvailabilityDropdownOptions = ( cacheTime: 1000 * 60 * 5, keepPreviousData: true, retry: false, - select: (data) => { - return data?.map((orgUnitType: OrgUnitType) => ({ + select: data => { + return ( + data?.map((orgUnitType: OrgUnitType) => ({ value: orgUnitType.id, label: orgUnitType.name, })) ?? [] + ); }, }, }); diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts index 2ad9b7a817..6b9de07aa7 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts @@ -3,7 +3,7 @@ import { getRequest } from '../../../../../libs/Api'; import { useSnackQuery } from '../../../../../libs/apiHooks'; import { apiUrlOUCRC } from '../../constants'; -import { OrgUnitChangeRequestConfigurationFull } from '../../types'; +import { OrgUnitChangeRequestConfigurationForm, OrgUnitChangeRequestConfigurationFull } from '../../types'; const retrieveOrgUnitChangeRequestConfig = (url: string) => { return getRequest(url) as Promise; @@ -11,7 +11,9 @@ const retrieveOrgUnitChangeRequestConfig = (url: string) => { export const useRetrieveOrgUnitChangeRequestConfig = ( configId?: number, -): UseQueryResult => { + // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function + onSuccess: (data: any) => void = _data => {}, +): UseQueryResult => { const url = `${apiUrlOUCRC}${configId}`; return useSnackQuery({ queryKey: ['useRetrieveOrgUnitChangeRequestConfig', url], @@ -21,6 +23,40 @@ export const useRetrieveOrgUnitChangeRequestConfig = ( staleTime: 1000 * 60 * 15, // in MS cacheTime: 1000 * 60 * 5, keepPreviousData: true, + onSuccess, + select: (data: OrgUnitChangeRequestConfigurationFull) => { + return { + projectId: data.project.id, + orgUnitTypeId: data.org_unit_type.id, + orgUnitsEditable: data.org_units_editable, + editableFields: data.editable_fields, + possibleTypeIds: data?.possible_types + ?.map(type => { + return type.id; + }) + .join(','), + possibleParentTypeIds: data?.possible_parent_types + ?.map(type => { + return type.id; + }) + .join(','), + groupSetIds: data?.group_sets + ?.map(type => { + return type.id; + }) + .join(','), + editableReferenceFormIds: data?.editable_reference_forms + ?.map(type => { + return type.id; + }) + .join(','), + otherGroupIds: data?.other_groups + ?.map(type => { + return type.id; + }) + .join(','), + }; + }, }, }); }; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts index 27e9c86136..dbf944dabb 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts @@ -3,7 +3,7 @@ import { patchRequest, postRequest } from '../../../../../libs/Api'; import { useSnackMutation } from '../../../../../libs/apiHooks'; import { apiUrlOUCRC, editableFieldsForBackend } from '../../constants'; -import { OrgUnitChangeRequestConfigurationFull } from '../../types'; +import { OrgUnitChangeRequestConfigurationForm, OrgUnitChangeRequestConfigurationFull } from '../../types'; function cleanEditableFieldsForSaving(editableFields) { if (!editableFields) { @@ -22,64 +22,78 @@ function cleanEditableFieldsForSaving(editableFields) { return cleanEditableFields; } -function mapValuesForSaving(values) { +function mapValuesForSaving(values: OrgUnitChangeRequestConfigurationForm) : Partial { const apiValues = { org_units_editable: values.orgUnitsEditable, }; // These two fields can't be updated so they are only set for creation if (!values.id) { - apiValues.project_id = values.projectId; - apiValues.org_unit_type_id = values.orgUnitTypeId; + apiValues.project_id = values.projectId; + apiValues.org_unit_type_id = values.orgUnitTypeId; } // This field must be cleaned because the backend accepts only some values - const cleanedEditableFields = cleanEditableFieldsForSaving(values.editableFields); + const cleanedEditableFields = cleanEditableFieldsForSaving( + values.editableFields, + ); if (cleanedEditableFields) { - apiValues.editable_fields = cleanedEditableFields; + apiValues.editable_fields = cleanedEditableFields; } // All the many to many fields are added if they have a value if (values.possibleTypeIds) { - apiValues.possible_type_ids = values.possibleTypeIds.split(",").map(Number); + apiValues.possible_type_ids = values.possibleTypeIds + .split(',') + .map(Number); } if (values.possibleParentTypeIds) { - apiValues.possible_parent_type_ids = values.possibleParentTypeIds.split(",").map(Number); + apiValues.possible_parent_type_ids = values.possibleParentTypeIds + .split(',') + .map(Number); } if (values.groupSetIds) { - apiValues.group_set_ids = values.groupSetIds.split(",").map(Number); + apiValues.group_set_ids = values.groupSetIds.split(',').map(Number); } if (values.editableReferenceFormIds) { - apiValues.editable_reference_form_ids = values.editableReferenceFormIds.split(",").map(Number); + apiValues.editable_reference_form_ids = values.editableReferenceFormIds + .split(',') + .map(Number); } if (values.otherGroupIds) { - apiValues.other_group_ids = values.otherGroupIds.split(",").map(Number); + apiValues.other_group_ids = values.otherGroupIds.split(',').map(Number); } return apiValues; -}; +} -const patchOrgUniType = async (body: Partial) => { - const url = `${apiUrlOUCRC}${body.id}/`; +const patchOrgUniType = async ( + configId: number, + body: Partial, +): Promise => { + const url = `${apiUrlOUCRC}${configId}/`; return patchRequest(url, body); }; -const postOrgUnitType = async (body: OrgUnitChangeRequestConfigurationFull) => { +const postOrgUnitType = async (body: Partial): Promise => { return postRequest({ url: `${apiUrlOUCRC}`, data: body, }); }; -export const useSaveOrgUnitChangeRequestConfiguration = (): UseMutationResult => { - const ignoreErrorCodes = [400]; - return useSnackMutation({ - mutationFn: (data: Partial) => { - const formattedData = mapValuesForSaving(data); - return formattedData.id - ? patchOrgUniType(formattedData) - : postOrgUnitType(formattedData as OrgUnitChangeRequestConfigurationFull); - }, - // invalidateQueryKey: ['paginated-orgunit-types'], - ignoreErrorCodes, - }); -}; +export const useSaveOrgUnitChangeRequestConfiguration = + (): UseMutationResult => { + const ignoreErrorCodes = [400]; + return useSnackMutation({ + mutationFn: ( + configId: number | undefined, + data: Partial, + ) => { + const formattedData = mapValuesForSaving(data); + return configId + ? patchOrgUniType(configId, formattedData) + : postOrgUnitType(formattedData); + }, + ignoreErrorCodes, + }); + }; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useOrgUnitEditableFieldsOptions.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useOrgUnitEditableFieldsOptions.ts index b5b97453af..9c55b5f8b4 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useOrgUnitEditableFieldsOptions.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useOrgUnitEditableFieldsOptions.ts @@ -3,12 +3,13 @@ import { editableFields } from '../constants'; import MESSAGES from '../messages'; import { DropdownOptions } from '../../../../types/utils'; -export const useOrgUnitsEditableFieldsOptions = (): DropdownOptions[] => { - const { formatMessage } = useSafeIntl(); - return editableFields.map(field => { - return { - value: field, - label: formatMessage(MESSAGES[field]), - }; - }); -}; +export const useOrgUnitsEditableFieldsOptions = + (): DropdownOptions[] => { + const { formatMessage } = useSafeIntl(); + return editableFields.map(field => { + return { + value: field, + label: formatMessage(MESSAGES[field]), + }; + }); + }; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts index f8b0cb33f2..cac3df097b 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts @@ -27,43 +27,67 @@ yup.addMethod( export const useValidationSchemaOUCRC = () => { const { formatMessage } = useSafeIntl(); return yup.object().shape({ - projectId: yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), - orgUnitTypeId: yup.string().nullable().required(formatMessage(MESSAGES.requiredField)), - orgUnitsEditable: yup.boolean().nullable().required(formatMessage(MESSAGES.requiredField)), + projectId: yup + .string() + .nullable() + .required(formatMessage(MESSAGES.requiredField)), + orgUnitTypeId: yup + .string() + .nullable() + .required(formatMessage(MESSAGES.requiredField)), + orgUnitsEditable: yup + .boolean() + .nullable() + .required(formatMessage(MESSAGES.requiredField)), editableFields: yup.string().nullable().when('orgUnitsEditable', { is: true, then: yup.string().nullable().required(), otherwise: yup.string().nullable(), }), - possibleTypeIds: yup.string().nullable().when('orgUnitsEditable', { - is: true, - // @ts-ignore - then: yup.string().nullable().isMultiSelectValid(formatMessage), - otherwise: yup.string().nullable(), - }), - possibleParentTypeIds: yup.string().nullable().when('orgUnitsEditable', { - is: true, - // @ts-ignore - then: yup.string().nullable().isMultiSelectValid(formatMessage), - otherwise: yup.string().nullable(), - }), - groupSetIds: yup.string().nullable().when('orgUnitsEditable', { - is: true, - // @ts-ignore - then: yup.string().nullable().isMultiSelectValid(formatMessage), - otherwise: yup.string().nullable(), - }), - editableReferenceFormIds: yup.string().nullable().when('orgUnitsEditable', { - is: true, - // @ts-ignore - then: yup.string().nullable().isMultiSelectValid(formatMessage), - otherwise: yup.string().nullable(), - }), - otherGroupIds: yup.string().nullable().when('orgUnitsEditable', { - is: true, - // @ts-ignore - then: yup.string().nullable().isMultiSelectValid(formatMessage), - otherwise: yup.string().nullable(), - }), + possibleTypeIds: yup + .string() + .nullable() + .when('orgUnitsEditable', { + is: true, + // @ts-ignore + then: yup.string().nullable().isMultiSelectValid(formatMessage), + otherwise: yup.string().nullable(), + }), + possibleParentTypeIds: yup + .string() + .nullable() + .when('orgUnitsEditable', { + is: true, + // @ts-ignore + then: yup.string().nullable().isMultiSelectValid(formatMessage), + otherwise: yup.string().nullable(), + }), + groupSetIds: yup + .string() + .nullable() + .when('orgUnitsEditable', { + is: true, + // @ts-ignore + then: yup.string().nullable().isMultiSelectValid(formatMessage), + otherwise: yup.string().nullable(), + }), + editableReferenceFormIds: yup + .string() + .nullable() + .when('orgUnitsEditable', { + is: true, + // @ts-ignore + then: yup.string().nullable().isMultiSelectValid(formatMessage), + otherwise: yup.string().nullable(), + }), + otherGroupIds: yup + .string() + .nullable() + .when('orgUnitsEditable', { + is: true, + // @ts-ignore + then: yup.string().nullable().isMultiSelectValid(formatMessage), + otherwise: yup.string().nullable(), + }), }); -}; \ No newline at end of file +}; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts index 142d009ea7..c4e8343abe 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts @@ -75,7 +75,8 @@ const MESSAGES = defineMessages({ }, oucrcCreateSecondStepModalTitle: { id: 'iaso.label.oucrcCreateModalTitle', - defaultMessage: 'OrgUnit Change Request Configuration - Creation 2nd step', + defaultMessage: + 'OrgUnit Change Request Configuration - Creation 2nd step', }, oucrcCreateUpdateModalTitle: { id: 'iaso.label.oucrcCreateModalTitle', diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts index 9628ea2eee..cf26a90548 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts @@ -48,14 +48,26 @@ export type OrgUnitChangeRequestConfigListElement = { export type OrgUnitChangeRequestConfigurationFull = { id: number; project: Project; - orgUnitType: OrgUnitType; + org_unit_type: OrgUnitType; + org_units_editable?: boolean; + editable_fields?: string; + possible_types?: Array; + possible_parent_types?: Array; + group_sets?: Array; + editable_reference_forms?: Array; + other_groups?: Array; +}; + +export type OrgUnitChangeRequestConfigurationForm = { + projectId: number; + orgUnitTypeId: number; orgUnitsEditable?: boolean; - editableFields?: Array; - possibleTypes?: Array; - possibleParentTypes?: Array; - groupSets?: Array; - editableReferenceForms?: Array; - otherGroups?: Array; + editableFields?: string; + possibleTypeIds?: string; + possibleParentTypeIds?: string; + groupSetIds?: string; + editableReferenceFormIds?: string; + otherGroupIds?: string; }; export type OrgUnitChangeRequestConfiguration = { From 16505862d4e7ef30af6af819c515bb68d937f288 Mon Sep 17 00:00:00 2001 From: Christophe Gerard Date: Thu, 26 Sep 2024 12:17:47 +0200 Subject: [PATCH 07/22] clean, types, fixes --- .eslintrc.json | 1 + .../Dialog/ConfirmDeleteModal.tsx | 6 +- ...x => OrgUnitChangeRequestConfigDialog.tsx} | 52 ++++---- .../OrgUnitChangeRequestConfigs.tsx | 18 +-- .../useRetrieveOrgUnitChangeRequestConfig.ts | 66 ++++++----- ...seSaveOrgUnitChangeRequestConfiguration.ts | 112 +++++++++--------- .../hooks/useValidationSchemaOUCRC.ts | 4 +- .../domains/orgUnits/configuration/types.ts | 2 +- 8 files changed, 144 insertions(+), 117 deletions(-) rename hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/{OrgUnitChangeRequestConfigDialogUpdate.tsx => OrgUnitChangeRequestConfigDialog.tsx} (92%) diff --git a/.eslintrc.json b/.eslintrc.json index 8968cf5e8f..e5d5724f64 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -147,6 +147,7 @@ ], "rules": { + "prefer-arrow-callback": "error", "react/function-component-definition": "off", "no-tabs": "off", "no-console":["error",{"allow":["warn","error"]}], diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/ConfirmDeleteModal.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/ConfirmDeleteModal.tsx index 275f6f9dfb..1a12c67253 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/ConfirmDeleteModal.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/ConfirmDeleteModal.tsx @@ -6,15 +6,15 @@ import { useSafeIntl, } from 'bluesquare-components'; -import MESSAGES from '../messages'; -import { OrgUnitChangeRequestConfig } from '../types'; import { DeleteIconButton } from '../../../../components/Buttons/DeleteIconButton'; import { useDeleteOrgUnitChangeRequestConfig } from '../hooks/api/useDeleteOrgUnitChangeRequestConfig'; +import MESSAGES from '../messages'; +import { OrgUnitChangeRequestConfigurationFull } from '../types'; type Props = { isOpen: boolean; closeDialog: () => void; - config: OrgUnitChangeRequestConfig; + config: OrgUnitChangeRequestConfigurationFull; }; const ConfirmDeleteModal: FunctionComponent = ({ diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx similarity index 92% rename from hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx rename to hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx index 894bab4316..ce89fafc5e 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogUpdate.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx @@ -1,27 +1,30 @@ -import React, { FunctionComponent, useCallback } from 'react'; +import { Typography } from '@mui/material'; import { ConfirmCancelModal, LoadingSpinner, makeFullModal, useSafeIntl, } from 'bluesquare-components'; -import { isEqual } from 'lodash'; -import { Typography } from '@mui/material'; import { useFormik } from 'formik'; -import { useTranslatedErrors } from '../../../../libs/validation'; -import MESSAGES from '../messages'; +import { isEqual } from 'lodash'; +import React, { FunctionComponent, useCallback } from 'react'; import { EditIconButton } from '../../../../components/Buttons/EditIconButton'; import InputComponent from '../../../../components/forms/InputComponent'; -import { editableFieldsManyToManyFields } from '../constants'; -import { useGetOrgUnitTypesDropdownOptions } from '../../orgUnitTypes/hooks/useGetOrgUnitTypesDropdownOptions'; +import { useTranslatedErrors } from '../../../../libs/validation'; import { useGetGroupDropdown } from '../../hooks/requests/useGetGroups'; +import { useGetOrgUnitTypesDropdownOptions } from '../../orgUnitTypes/hooks/useGetOrgUnitTypesDropdownOptions'; +import { editableFieldsManyToManyFields } from '../constants'; import { useGetFormDropdownOptions } from '../hooks/api/useGetFormDropdownOptions'; +import { useRetrieveOrgUnitChangeRequestConfig } from '../hooks/api/useRetrieveOrgUnitChangeRequestConfig'; import { useSaveOrgUnitChangeRequestConfiguration } from '../hooks/api/useSaveOrgUnitChangeRequestConfiguration'; -import { useOrgUnitsEditableOptions } from '../hooks/useOrgUnitsEditableOptions'; import { useOrgUnitsEditableFieldsOptions } from '../hooks/useOrgUnitEditableFieldsOptions'; -import { OrgUnitChangeRequestConfiguration, OrgUnitChangeRequestConfigurationForm } from '../types'; -import { useRetrieveOrgUnitChangeRequestConfig } from '../hooks/api/useRetrieveOrgUnitChangeRequestConfig'; +import { useOrgUnitsEditableOptions } from '../hooks/useOrgUnitsEditableOptions'; import { useValidationSchemaOUCRC } from '../hooks/useValidationSchemaOUCRC'; +import MESSAGES from '../messages'; +import { + OrgUnitChangeRequestConfiguration, + OrgUnitChangeRequestConfigurationForm, +} from '../types'; type Props = { config: OrgUnitChangeRequestConfiguration; @@ -29,7 +32,7 @@ type Props = { closeDialog: () => void; }; -const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ +const OrgUnitChangeRequestConfigDialog: FunctionComponent = ({ config, isOpen, closeDialog, @@ -44,7 +47,7 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ errors, touched, setFieldTouched, - resetForm, + setValues, } = useFormik({ initialValues: { projectId: config.project.id, @@ -58,18 +61,24 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ otherGroupIds: undefined, }, validationSchema: configValidationSchema, - onSubmit: () => { - saveConfig(config.id, values); + onSubmit: (newValues: OrgUnitChangeRequestConfigurationForm) => { + saveConfig({ + configId: config.id, + data: newValues, + }); closeDialog(); }, }); const { isLoading: isLoadingFullConfig } = useRetrieveOrgUnitChangeRequestConfig(config?.id, fetchedConfig => { - console.log("*** before resetting form - fetchedConfig ", fetchedConfig); - resetForm(fetchedConfig); - }); + // eslint-disable-next-line no-console + console.log( + '*** before resetting form - fetchedConfig ', + fetchedConfig, + ); - console.log("*** values = ", values); + setValues(fetchedConfig); + }); const { data: orgUnitTypeOptions } = useGetOrgUnitTypesDropdownOptions(); const { data: groupOptions } = useGetGroupDropdown({}); @@ -254,11 +263,12 @@ const OrgUnitChangeRequestConfigDialogUpdate: FunctionComponent = ({ }; const modalWithButton = makeFullModal( - OrgUnitChangeRequestConfigDialogUpdate, + OrgUnitChangeRequestConfigDialog, EditIconButton, ); export { - modalWithButton as OrgUnitChangeRequestConfigDialogUpdate, - OrgUnitChangeRequestConfigDialogUpdate as OrgUnitChangeRequestConfigDialogCreateSecondStep, + OrgUnitChangeRequestConfigDialog as OrgUnitChangeRequestConfigDialogCreateSecondStep, + modalWithButton as OrgUnitChangeRequestConfigDialogUpdate }; + diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx index 04c2c0da23..362a5deb7a 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/OrgUnitChangeRequestConfigs.tsx @@ -1,20 +1,20 @@ +import { Box } from '@mui/material'; import { makeStyles } from '@mui/styles'; import { commonStyles, useSafeIntl } from 'bluesquare-components'; import React, { FunctionComponent, useCallback, useState } from 'react'; -import { Box } from '@mui/material'; -import { useParamsObject } from '../../../routing/hooks/useParamsObject'; +import TopBar from '../../../components/nav/TopBarComponent'; import { baseUrls } from '../../../constants/urls'; +import { useParamsObject } from '../../../routing/hooks/useParamsObject'; +import { OrgUnitChangeRequestConfigDialogCreateSecondStep } from './Dialog/OrgUnitChangeRequestConfigDialog'; +import { OrgUnitChangeRequestConfigDialogCreateFirstStep } from './Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep'; +import { OrgUnitChangeRequestConfigsFilter } from './Filter/OrgUnitChangeRequestConfigsFilter'; +import { useGetOrgUnitChangeRequestConfigs } from './hooks/api/useGetOrgUnitChangeRequestConfigs'; +import MESSAGES from './messages'; +import { OrgUnitChangeRequestConfigsTable } from './Tables/OrgUnitChangeRequestConfigsTable'; import { OrgUnitChangeRequestConfigsParams, OrgUnitChangeRequestConfiguration, } from './types'; -import { useGetOrgUnitChangeRequestConfigs } from './hooks/api/useGetOrgUnitChangeRequestConfigs'; -import TopBar from '../../../components/nav/TopBarComponent'; -import MESSAGES from './messages'; -import { OrgUnitChangeRequestConfigsFilter } from './Filter/OrgUnitChangeRequestConfigsFilter'; -import { OrgUnitChangeRequestConfigsTable } from './Tables/OrgUnitChangeRequestConfigsTable'; -import { OrgUnitChangeRequestConfigDialogCreateFirstStep } from './Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep'; -import { OrgUnitChangeRequestConfigDialogCreateSecondStep } from './Dialog/OrgUnitChangeRequestConfigDialogUpdate'; const useStyles = makeStyles(theme => ({ ...commonStyles(theme), diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts index 6b9de07aa7..6ce7ee188b 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts @@ -3,18 +3,22 @@ import { getRequest } from '../../../../../libs/Api'; import { useSnackQuery } from '../../../../../libs/apiHooks'; import { apiUrlOUCRC } from '../../constants'; -import { OrgUnitChangeRequestConfigurationForm, OrgUnitChangeRequestConfigurationFull } from '../../types'; +import { + OrgUnitChangeRequestConfigurationForm, + OrgUnitChangeRequestConfigurationFull, +} from '../../types'; const retrieveOrgUnitChangeRequestConfig = (url: string) => { return getRequest(url) as Promise; }; +const mapAndJoin = (items: any[] = []) => items?.map(item => item.id).join(','); export const useRetrieveOrgUnitChangeRequestConfig = ( configId?: number, // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function onSuccess: (data: any) => void = _data => {}, ): UseQueryResult => { - const url = `${apiUrlOUCRC}${configId}`; + const url = `${apiUrlOUCRC}${configId}/`; return useSnackQuery({ queryKey: ['useRetrieveOrgUnitChangeRequestConfig', url], queryFn: () => retrieveOrgUnitChangeRequestConfig(url), @@ -25,36 +29,42 @@ export const useRetrieveOrgUnitChangeRequestConfig = ( keepPreviousData: true, onSuccess, select: (data: OrgUnitChangeRequestConfigurationFull) => { + const editableFields: string[] = ( + data.editable_fields || [] + ).map(field => { + if (field === 'opening_date') return 'openingDate'; + if (field === 'closed_date') return 'closedDate'; + return field; + }); + if ((data?.possible_types ?? []).length > 0) { + editableFields.push('possibleTypeIds'); + } + if ((data?.possible_parent_types ?? []).length > 0) { + editableFields.push('possibleParentTypeIds'); + } + if ((data?.group_sets ?? []).length > 0) { + editableFields.push('groupSetIds'); + } + if ((data?.editable_reference_forms ?? []).length > 0) { + editableFields.push('editableReferenceFormIds'); + } + if ((data?.other_groups ?? []).length > 0) { + editableFields.push('otherGroupIds'); + } return { projectId: data.project.id, orgUnitTypeId: data.org_unit_type.id, orgUnitsEditable: data.org_units_editable, - editableFields: data.editable_fields, - possibleTypeIds: data?.possible_types - ?.map(type => { - return type.id; - }) - .join(','), - possibleParentTypeIds: data?.possible_parent_types - ?.map(type => { - return type.id; - }) - .join(','), - groupSetIds: data?.group_sets - ?.map(type => { - return type.id; - }) - .join(','), - editableReferenceFormIds: data?.editable_reference_forms - ?.map(type => { - return type.id; - }) - .join(','), - otherGroupIds: data?.other_groups - ?.map(type => { - return type.id; - }) - .join(','), + editableFields, + possibleTypeIds: mapAndJoin(data.possible_types), + possibleParentTypeIds: mapAndJoin( + data.possible_parent_types, + ), + groupSetIds: mapAndJoin(data.group_sets), + editableReferenceFormIds: mapAndJoin( + data.editable_reference_forms, + ), + otherGroupIds: mapAndJoin(data.other_groups), }; }, }, diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts index dbf944dabb..8d6090f595 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts @@ -3,78 +3,77 @@ import { patchRequest, postRequest } from '../../../../../libs/Api'; import { useSnackMutation } from '../../../../../libs/apiHooks'; import { apiUrlOUCRC, editableFieldsForBackend } from '../../constants'; -import { OrgUnitChangeRequestConfigurationForm, OrgUnitChangeRequestConfigurationFull } from '../../types'; +import { OrgUnitChangeRequestConfigurationForm } from '../../types'; -function cleanEditableFieldsForSaving(editableFields) { +const cleanEditableFieldsForSaving = (editableFields?: string): string[] => { if (!editableFields) { - return editableFields; + return []; } - let cleanEditableFields = ''; - const splitFields = editableFields.split(','); - for (const field in splitFields) { - if (editableFieldsForBackend.includes(field)) { - cleanEditableFields += `${field},`; - } - } - if (cleanEditableFields.length) { - cleanEditableFields = cleanEditableFields.slice(0, -1); - } - return cleanEditableFields; -} + return editableFields + .split(',') + .filter(field => editableFieldsForBackend.includes(field)) + .map(field => { + if (field === 'openingDate') return 'opening_date'; + if (field === 'closedDate') return 'closed_date'; + return field; + }); +}; -function mapValuesForSaving(values: OrgUnitChangeRequestConfigurationForm) : Partial { - const apiValues = { - org_units_editable: values.orgUnitsEditable, - }; +type ApiValues = { + org_units_editable: boolean; + project_id?: number; + org_unit_type_id?: number; + editable_fields?: string[]; + possible_type_ids?: number[]; + possible_parent_type_ids?: number[]; + group_set_ids?: number[]; + editable_reference_form_ids?: number[]; + other_group_ids?: number[]; +}; +const mapValuesForSaving = ( + configId: number | undefined, + values: OrgUnitChangeRequestConfigurationForm, +): ApiValues => { + const apiValues: ApiValues = { + org_units_editable: values.orgUnitsEditable ?? false, + }; // These two fields can't be updated so they are only set for creation - if (!values.id) { + if (configId) { apiValues.project_id = values.projectId; apiValues.org_unit_type_id = values.orgUnitTypeId; } // This field must be cleaned because the backend accepts only some values - const cleanedEditableFields = cleanEditableFieldsForSaving( + + apiValues.editable_fields = cleanEditableFieldsForSaving( values.editableFields, ); - if (cleanedEditableFields) { - apiValues.editable_fields = cleanedEditableFields; - } // All the many to many fields are added if they have a value - if (values.possibleTypeIds) { - apiValues.possible_type_ids = values.possibleTypeIds - .split(',') - .map(Number); - } - if (values.possibleParentTypeIds) { - apiValues.possible_parent_type_ids = values.possibleParentTypeIds - .split(',') - .map(Number); - } - if (values.groupSetIds) { - apiValues.group_set_ids = values.groupSetIds.split(',').map(Number); - } - if (values.editableReferenceFormIds) { - apiValues.editable_reference_form_ids = values.editableReferenceFormIds - .split(',') - .map(Number); - } - if (values.otherGroupIds) { - apiValues.other_group_ids = values.otherGroupIds.split(',').map(Number); - } + const splitAndMapToNumbers = (str?: string) => str?.split(',').map(Number); + + apiValues.possible_type_ids = splitAndMapToNumbers(values.possibleTypeIds); + apiValues.possible_parent_type_ids = splitAndMapToNumbers( + values.possibleParentTypeIds, + ); + apiValues.group_set_ids = splitAndMapToNumbers(values.groupSetIds); + apiValues.editable_reference_form_ids = splitAndMapToNumbers( + values.editableReferenceFormIds, + ); + apiValues.other_group_ids = splitAndMapToNumbers(values.otherGroupIds); return apiValues; -} +}; const patchOrgUniType = async ( configId: number, - body: Partial, + body: ApiValues, ): Promise => { const url = `${apiUrlOUCRC}${configId}/`; return patchRequest(url, body); }; -const postOrgUnitType = async (body: Partial): Promise => { +const postOrgUnitType = async (body: ApiValues): Promise => { return postRequest({ url: `${apiUrlOUCRC}`, data: body, @@ -85,15 +84,22 @@ export const useSaveOrgUnitChangeRequestConfiguration = (): UseMutationResult => { const ignoreErrorCodes = [400]; return useSnackMutation({ - mutationFn: ( - configId: number | undefined, - data: Partial, - ) => { - const formattedData = mapValuesForSaving(data); + mutationFn: ({ + configId, + data, + }: { + configId: number | undefined; + data: OrgUnitChangeRequestConfigurationForm; + }) => { + const formattedData = mapValuesForSaving(configId, data); return configId ? patchOrgUniType(configId, formattedData) : postOrgUnitType(formattedData); }, ignoreErrorCodes, + invalidateQueryKey: [ + 'useRetrieveOrgUnitChangeRequestConfig', + 'getOrgUnitChangeRequestConfigs', + ], }); }; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts index cac3df097b..121f9a4798 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts @@ -1,5 +1,5 @@ -import * as yup from 'yup'; import { useSafeIntl } from 'bluesquare-components'; +import * as yup from 'yup'; import MESSAGES from '../messages'; yup.addMethod( @@ -11,7 +11,7 @@ yup.addMethod( if (!parent.editableFields) { return true; } - const splitFields = parent.editableFields.split(','); + const splitFields = parent.editableFields; const isFieldPopulated = parent[path]; if (!isFieldPopulated && splitFields.includes(path)) { return createError({ diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts index cf26a90548..0b8e5a697b 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/types.ts @@ -50,7 +50,7 @@ export type OrgUnitChangeRequestConfigurationFull = { project: Project; org_unit_type: OrgUnitType; org_units_editable?: boolean; - editable_fields?: string; + editable_fields?: string[]; possible_types?: Array; possible_parent_types?: Array; group_sets?: Array; From 37f5601a4d7c580fc0b3359d78f1e9f0c7a635c3 Mon Sep 17 00:00:00 2001 From: Christophe Gerard Date: Thu, 26 Sep 2024 14:01:26 +0200 Subject: [PATCH 08/22] working good --- .../OrgUnitChangeRequestConfigDialog.tsx | 18 +++--- ...angeRequestConfigDialogCreateFirstStep.tsx | 12 ++-- .../Tables/EditableFieldsCell.tsx | 35 ++++++++++++ .../OrgUnitChangeRequestConfigsTable.tsx | 18 +++--- .../useRetrieveOrgUnitChangeRequestConfig.ts | 57 ++++++++++--------- .../Iaso/domains/projects/hooks/requests.ts | 19 +++---- .../serializers.py | 23 ++++---- .../views.py | 8 +-- 8 files changed, 112 insertions(+), 78 deletions(-) create mode 100644 hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/EditableFieldsCell.tsx diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx index ce89fafc5e..4be0d6a191 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx @@ -7,7 +7,7 @@ import { } from 'bluesquare-components'; import { useFormik } from 'formik'; import { isEqual } from 'lodash'; -import React, { FunctionComponent, useCallback } from 'react'; +import React, { FunctionComponent, useCallback, useEffect } from 'react'; import { EditIconButton } from '../../../../components/Buttons/EditIconButton'; import InputComponent from '../../../../components/forms/InputComponent'; import { useTranslatedErrors } from '../../../../libs/validation'; @@ -69,17 +69,13 @@ const OrgUnitChangeRequestConfigDialog: FunctionComponent = ({ closeDialog(); }, }); - const { isLoading: isLoadingFullConfig } = - useRetrieveOrgUnitChangeRequestConfig(config?.id, fetchedConfig => { - // eslint-disable-next-line no-console - console.log( - '*** before resetting form - fetchedConfig ', - fetchedConfig, - ); - + const { data: fetchedConfig, isLoading: isLoadingFullConfig } = + useRetrieveOrgUnitChangeRequestConfig(config?.id); + useEffect(() => { + if (fetchedConfig) { setValues(fetchedConfig); - }); - + } + }, [fetchedConfig, setValues]); const { data: orgUnitTypeOptions } = useGetOrgUnitTypesDropdownOptions(); const { data: groupOptions } = useGetGroupDropdown({}); const { data: formOptions } = useGetFormDropdownOptions( diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx index c2fb30f8e5..e6c6923849 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialogCreateFirstStep.tsx @@ -1,18 +1,18 @@ -import React, { FunctionComponent, useCallback } from 'react'; import { + AddButton, ConfirmCancelModal, makeFullModal, useSafeIntl, - AddButton, } from 'bluesquare-components'; -import * as Yup from 'yup'; import { useFormik } from 'formik'; import { isEqual } from 'lodash'; +import React, { FunctionComponent, useCallback } from 'react'; +import * as Yup from 'yup'; +import InputComponent from '../../../../components/forms/InputComponent'; import { useTranslatedErrors } from '../../../../libs/validation'; -import MESSAGES from '../messages'; import { useGetProjectsDropdownOptions } from '../../../projects/hooks/requests'; -import InputComponent from '../../../../components/forms/InputComponent'; import { useGetOUCRCCheckAvailabilityDropdownOptions } from '../hooks/api/useGetOUCRCCheckAvailabilityDropdownOptions'; +import MESSAGES from '../messages'; type Props = { isOpen: boolean; @@ -86,7 +86,7 @@ const OrgUnitChangeRequestConfigDialogCreateFirstStep: FunctionComponent< }); const { data: allProjects, isFetching: isFetchingProjects } = - useGetProjectsDropdownOptions(); + useGetProjectsDropdownOptions(false); const { data: orgUnitTypeOptions, isFetching: isFetchingOrgUnitTypes } = useGetOUCRCCheckAvailabilityDropdownOptions(values.projectId); diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/EditableFieldsCell.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/EditableFieldsCell.tsx new file mode 100644 index 0000000000..9a1acca869 --- /dev/null +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/EditableFieldsCell.tsx @@ -0,0 +1,35 @@ +/* eslint-disable camelcase */ +import { Box, Chip } from '@mui/material'; +import { textPlaceholder, useSafeIntl } from 'bluesquare-components'; +import React from 'react'; +import { computeEditableFields } from '../hooks/api/useRetrieveOrgUnitChangeRequestConfig'; +import MESSAGES from '../messages'; +import { OrgUnitChangeRequestConfigurationFull } from '../types'; + +export const EditableFieldsCell = ({ + row: { original }, +}: { + row: { original: OrgUnitChangeRequestConfigurationFull }; +}) => { + const { formatMessage } = useSafeIntl(); + const editableFields = computeEditableFields(original); + + if (editableFields.length === 0) { + return textPlaceholder; + } + + return ( + + {editableFields.map(field => ( + + ))} + + ); +}; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/OrgUnitChangeRequestConfigsTable.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/OrgUnitChangeRequestConfigsTable.tsx index 7aed077584..2f34275268 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/OrgUnitChangeRequestConfigsTable.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/OrgUnitChangeRequestConfigsTable.tsx @@ -1,22 +1,23 @@ +import { Column, useSafeIntl } from 'bluesquare-components'; import React, { - FunctionComponent, Dispatch, + FunctionComponent, SetStateAction, - useMemo, useCallback, + useMemo, } from 'react'; -import { Column, useSafeIntl } from 'bluesquare-components'; +import { EditIconButton } from '../../../../components/Buttons/EditIconButton'; +import { DateTimeCell } from '../../../../components/Cells/DateTimeCell'; import { TableWithDeepLink } from '../../../../components/tables/TableWithDeepLink'; import { baseUrls } from '../../../../constants/urls'; +import { ConfirmDeleteModal } from '../Dialog/ConfirmDeleteModal'; +import MESSAGES from '../messages'; import { OrgUnitChangeRequestConfigsPaginated, OrgUnitChangeRequestConfigsParams, OrgUnitChangeRequestConfiguration, } from '../types'; -import MESSAGES from '../messages'; -import { DateTimeCell } from '../../../../components/Cells/DateTimeCell'; -import { EditIconButton } from '../../../../components/Buttons/EditIconButton'; -import { ConfirmDeleteModal } from '../Dialog/ConfirmDeleteModal'; +import { EditableFieldsCell } from './EditableFieldsCell'; const useColumns = ( onEditClicked: Dispatch>, @@ -56,7 +57,8 @@ const useColumns = ( { Header: formatMessage(MESSAGES.editable_fields), id: 'editable_fields', - width: 600, + sortable: false, + Cell: EditableFieldsCell, }, { Header: formatMessage(MESSAGES.actions), diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts index 6ce7ee188b..9f1a99aee1 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts @@ -12,50 +12,53 @@ const retrieveOrgUnitChangeRequestConfig = (url: string) => { return getRequest(url) as Promise; }; +const fieldMapping: { [key: string]: string } = { + opening_date: 'openingDate', + closed_date: 'closedDate', +}; + +const conditionalFields = [ + { key: 'possible_types', name: 'possibleTypeIds' }, + { key: 'possible_parent_types', name: 'possibleParentTypeIds' }, + { key: 'group_sets', name: 'groupSetIds' }, + { key: 'editable_reference_forms', name: 'editableReferenceFormIds' }, + { key: 'other_groups', name: 'otherGroupIds' }, +]; + +export const computeEditableFields = ( + data: OrgUnitChangeRequestConfigurationFull, +): string[] => { + const editableFields: string[] = (data.editable_fields || []).map(field => { + return fieldMapping[field] || field; + }); + + conditionalFields.forEach(({ key, name }) => { + if ((data[key] ?? []).length > 0) { + editableFields.push(name); + } + }); + + return editableFields; +}; const mapAndJoin = (items: any[] = []) => items?.map(item => item.id).join(','); export const useRetrieveOrgUnitChangeRequestConfig = ( configId?: number, - // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function - onSuccess: (data: any) => void = _data => {}, ): UseQueryResult => { const url = `${apiUrlOUCRC}${configId}/`; return useSnackQuery({ - queryKey: ['useRetrieveOrgUnitChangeRequestConfig', url], + queryKey: ['useRetrieveOrgUnitChangeRequestConfig', configId], queryFn: () => retrieveOrgUnitChangeRequestConfig(url), options: { enabled: Boolean(configId), staleTime: 1000 * 60 * 15, // in MS cacheTime: 1000 * 60 * 5, keepPreviousData: true, - onSuccess, select: (data: OrgUnitChangeRequestConfigurationFull) => { - const editableFields: string[] = ( - data.editable_fields || [] - ).map(field => { - if (field === 'opening_date') return 'openingDate'; - if (field === 'closed_date') return 'closedDate'; - return field; - }); - if ((data?.possible_types ?? []).length > 0) { - editableFields.push('possibleTypeIds'); - } - if ((data?.possible_parent_types ?? []).length > 0) { - editableFields.push('possibleParentTypeIds'); - } - if ((data?.group_sets ?? []).length > 0) { - editableFields.push('groupSetIds'); - } - if ((data?.editable_reference_forms ?? []).length > 0) { - editableFields.push('editableReferenceFormIds'); - } - if ((data?.other_groups ?? []).length > 0) { - editableFields.push('otherGroupIds'); - } return { projectId: data.project.id, orgUnitTypeId: data.org_unit_type.id, orgUnitsEditable: data.org_units_editable, - editableFields, + editableFields: computeEditableFields(data).join(','), possibleTypeIds: mapAndJoin(data.possible_types), possibleParentTypeIds: mapAndJoin( data.possible_parent_types, diff --git a/hat/assets/js/apps/Iaso/domains/projects/hooks/requests.ts b/hat/assets/js/apps/Iaso/domains/projects/hooks/requests.ts index d0ef3de92e..8086803715 100644 --- a/hat/assets/js/apps/Iaso/domains/projects/hooks/requests.ts +++ b/hat/assets/js/apps/Iaso/domains/projects/hooks/requests.ts @@ -1,12 +1,12 @@ -import { UseQueryResult, UseMutationResult, useQueryClient } from 'react-query'; -import { UrlParams, ApiParams } from 'bluesquare-components'; +import { ApiParams, UrlParams } from 'bluesquare-components'; +import { UseMutationResult, useQueryClient, UseQueryResult } from 'react-query'; import { getRequest, postRequest, putRequest } from '../../../libs/Api'; -import { useSnackQuery, useSnackMutation } from '../../../libs/apiHooks'; +import { useSnackMutation, useSnackQuery } from '../../../libs/apiHooks'; +import { DropdownOptions } from '../../../types/utils'; +import { FeatureFlag } from '../types/featureFlag'; import { PaginatedProjects } from '../types/paginatedProjects'; import { Project } from '../types/project'; -import { FeatureFlag } from '../types/featureFlag'; -import { DropdownOptions } from '../../../types/utils'; type ProjectApi = { projects: Array; @@ -15,10 +15,9 @@ const getProjects = (): Promise => { return getRequest('/api/projects/'); }; -export const useGetProjectsDropdownOptions = (): UseQueryResult< - DropdownOptions[], - Error -> => { +export const useGetProjectsDropdownOptions = ( + asString = true, +): UseQueryResult[], Error> => { const queryClient = useQueryClient(); const queryKey: any[] = ['projects-dropdown']; return useSnackQuery(queryKey, () => getProjects(), undefined, { @@ -32,7 +31,7 @@ export const useGetProjectsDropdownOptions = (): UseQueryResult< if (!data) return []; return data.projects.map(project => { return { - value: project.id.toString(), + value: asString ? project.id?.toString() : project.id, label: project.name, }; }); diff --git a/iaso/api/org_unit_change_request_configurations/serializers.py b/iaso/api/org_unit_change_request_configurations/serializers.py index 7e65f5f6c4..b76857fc9a 100644 --- a/iaso/api/org_unit_change_request_configurations/serializers.py +++ b/iaso/api/org_unit_change_request_configurations/serializers.py @@ -1,27 +1,20 @@ import django.core.serializers +from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from rest_framework import serializers -from django.contrib.auth.models import User from hat.audit.audit_logger import AuditLogger from hat.audit.models import ORG_UNIT_CHANGE_REQUEST_CONFIGURATION_API +from iaso.api.common import TimestampField from iaso.api.org_unit_change_request_configurations.validation import ( - validate_org_unit_types, - validate_group_sets, validate_forms, + validate_group_sets, validate_groups, + validate_org_unit_types, ) from iaso.api.query_params import PROJECT_ID -from iaso.models import ( - OrgUnitType, - OrgUnitChangeRequestConfiguration, - Project, - GroupSet, - Form, - Group, -) +from iaso.models import Form, Group, GroupSet, OrgUnitChangeRequestConfiguration, OrgUnitType, Project from iaso.utils.serializer.id_or_uuid_field import IdOrUuidRelatedField -from iaso.api.common import TimestampField class UserNestedSerializer(serializers.ModelSerializer): @@ -126,6 +119,12 @@ class Meta: "created_at", "updated_by", "updated_at", + "editable_fields", + "possible_types", + "possible_parent_types", + "group_sets", + "editable_reference_forms", + "other_groups", ] diff --git a/iaso/api/org_unit_change_request_configurations/views.py b/iaso/api/org_unit_change_request_configurations/views.py index e89ef0f8e9..f099826b34 100644 --- a/iaso/api/org_unit_change_request_configurations/views.py +++ b/iaso/api/org_unit_change_request_configurations/views.py @@ -1,22 +1,22 @@ import django_filters from django.db.models import Q -from rest_framework import viewsets, filters, serializers +from rest_framework import filters, serializers, viewsets from rest_framework.decorators import action from rest_framework.response import Response from iaso.api.org_unit_change_request_configurations.filters import OrgUnitChangeRequestConfigurationListFilter from iaso.api.org_unit_change_request_configurations.pagination import OrgUnitChangeRequestConfigurationPagination from iaso.api.org_unit_change_request_configurations.permissions import ( - HasOrgUnitsChangeRequestConfigurationReadPermission, HasOrgUnitsChangeRequestConfigurationFullPermission, + HasOrgUnitsChangeRequestConfigurationReadPermission, ) from iaso.api.org_unit_change_request_configurations.serializers import ( + OrgUnitChangeRequestConfigurationAuditLogger, OrgUnitChangeRequestConfigurationListSerializer, OrgUnitChangeRequestConfigurationRetrieveSerializer, - OrgUnitChangeRequestConfigurationWriteSerializer, OrgUnitChangeRequestConfigurationUpdateSerializer, + OrgUnitChangeRequestConfigurationWriteSerializer, OrgUnitTypeNestedSerializer, - OrgUnitChangeRequestConfigurationAuditLogger, ProjectIdSerializer, ) from iaso.models import OrgUnitChangeRequestConfiguration, OrgUnitType From 98b47f258a5bce4c71ebbba26dc5ab5dde143721 Mon Sep 17 00:00:00 2001 From: Christophe Gerard Date: Thu, 26 Sep 2024 14:31:27 +0200 Subject: [PATCH 09/22] fix translations and menu duplicate key --- hat/assets/js/apps/Iaso/constants/menu.tsx | 64 +++++++++---------- hat/assets/js/apps/Iaso/constants/urls.ts | 4 +- .../Iaso/domains/app/translations/en.json | 9 +++ .../Iaso/domains/app/translations/fr.json | 9 +++ .../orgUnits/configuration/messages.ts | 26 ++++---- 5 files changed, 66 insertions(+), 46 deletions(-) diff --git a/hat/assets/js/apps/Iaso/constants/menu.tsx b/hat/assets/js/apps/Iaso/constants/menu.tsx index 9f401aa4f7..d6fb13713d 100644 --- a/hat/assets/js/apps/Iaso/constants/menu.tsx +++ b/hat/assets/js/apps/Iaso/constants/menu.tsx @@ -1,60 +1,60 @@ /* eslint-disable react/jsx-props-no-spreading */ import React, { useContext, useMemo } from 'react'; -import DataSourceIcon from '@mui/icons-material/ListAltTwoTone'; -import Link from '@mui/icons-material/Link'; -import FormatListBulleted from '@mui/icons-material/FormatListBulleted'; -import Input from '@mui/icons-material/Input'; +import AccountBalanceIcon from '@mui/icons-material/AccountBalance'; +import AssessmentIcon from '@mui/icons-material/Assessment'; +import AssignmentIcon from '@mui/icons-material/Assignment'; +import AssignmentRoundedIcon from '@mui/icons-material/AssignmentRounded'; +import BookIcon from '@mui/icons-material/Book'; +import CategoryIcon from '@mui/icons-material/Category'; import CompareArrows from '@mui/icons-material/CompareArrows'; -import SupervisorAccount from '@mui/icons-material/SupervisorAccount'; -import GroupsIcon from '@mui/icons-material/Groups'; -import PhonelinkSetupIcon from '@mui/icons-material/PhonelinkSetup'; import DnsRoundedIcon from '@mui/icons-material/DnsRounded'; import DoneAll from '@mui/icons-material/DoneAll'; -import Settings from '@mui/icons-material/Settings'; +import FileCopyIcon from '@mui/icons-material/FileCopy'; +import FormatListBulleted from '@mui/icons-material/FormatListBulleted'; +import GroupIcon from '@mui/icons-material/Group'; +import GroupsIcon from '@mui/icons-material/Groups'; import GroupWork from '@mui/icons-material/GroupWork'; -import CategoryIcon from '@mui/icons-material/Category'; -import AssignmentRoundedIcon from '@mui/icons-material/AssignmentRounded'; +import HistoryIcon from '@mui/icons-material/History'; import ImportantDevicesRoundedIcon from '@mui/icons-material/ImportantDevicesRounded'; -import BookIcon from '@mui/icons-material/Book'; -import AssessmentIcon from '@mui/icons-material/Assessment'; -import GroupIcon from '@mui/icons-material/Group'; -import AssignmentIcon from '@mui/icons-material/Assignment'; -import StorageIcon from '@mui/icons-material/Storage'; +import Input from '@mui/icons-material/Input'; +import Link from '@mui/icons-material/Link'; +import DataSourceIcon from '@mui/icons-material/ListAltTwoTone'; import MenuBookIcon from '@mui/icons-material/MenuBook'; -import FileCopyIcon from '@mui/icons-material/FileCopy'; -import ViewModuleIcon from '@mui/icons-material/ViewModule'; import PaymentsIcon from '@mui/icons-material/Payments'; +import PhonelinkSetupIcon from '@mui/icons-material/PhonelinkSetup'; import PriceCheckIcon from '@mui/icons-material/PriceCheck'; -import AccountBalanceIcon from '@mui/icons-material/AccountBalance'; -import HistoryIcon from '@mui/icons-material/History'; +import Settings from '@mui/icons-material/Settings'; +import StorageIcon from '@mui/icons-material/Storage'; +import SupervisorAccount from '@mui/icons-material/SupervisorAccount'; +import ViewModuleIcon from '@mui/icons-material/ViewModule'; import { IntlFormatMessage, useSafeIntl } from 'bluesquare-components'; -import OrgUnitSvg from '../components/svg/OrgUnitSvgComponent'; import BeneficiarySvg from '../components/svg/Beneficiary'; import DHIS2Svg from '../components/svg/DHIS2SvgComponent'; -import * as paths from './routes'; +import OrgUnitSvg from '../components/svg/OrgUnitSvgComponent'; +import { locationLimitMax } from '../domains/orgUnits/constants/orgUnitConstants'; import { hasFeatureFlag, - SHOW_PAGES, - SHOW_DHIS2_LINK, SHOW_BENEFICIARY_TYPES_IN_LIST_MENU, SHOW_DEV_FEATURES, + SHOW_DHIS2_LINK, + SHOW_PAGES, } from '../utils/featureFlags'; -import { locationLimitMax } from '../domains/orgUnits/constants/orgUnitConstants'; +import * as paths from './routes'; -import MESSAGES from './messages'; -import { useCurrentUser } from '../utils/usersUtils'; +import { MenuItem, MenuItems, Plugins } from '../domains/app/types'; +import { useGetBeneficiaryTypesDropdown } from '../domains/entities/hooks/requests'; +import { useGetOrgunitsExtraPath } from '../domains/home/hooks/useGetOrgunitsExtraPath'; import { listMenuPermission, userHasOneOfPermissions, } from '../domains/users/utils'; -import { PluginsContext } from '../utils'; -import { useGetBeneficiaryTypesDropdown } from '../domains/entities/hooks/requests'; import { DropdownOptions } from '../types/utils'; -import { MenuItem, MenuItems, Plugins } from '../domains/app/types'; -import { useGetOrgunitsExtraPath } from '../domains/home/hooks/useGetOrgunitsExtraPath'; -import { CHANGE_REQUEST, CONFIGURATION } from './urls'; +import { PluginsContext } from '../utils'; +import { useCurrentUser } from '../utils/usersUtils'; +import MESSAGES from './messages'; +import { CHANGE_REQUEST, CHANGE_REQUEST_CONFIG, CONFIGURATION } from './urls'; // !! remove permission property if the menu has a subMenu !! const menuItems = ( @@ -199,7 +199,7 @@ const menuItems = ( }, { label: formatMessage(MESSAGES.configuration), - key: CHANGE_REQUEST, + key: CHANGE_REQUEST_CONFIG, icon: props => , subMenu: [ { diff --git a/hat/assets/js/apps/Iaso/constants/urls.ts b/hat/assets/js/apps/Iaso/constants/urls.ts index 7525c7eb67..eea6bef1cd 100644 --- a/hat/assets/js/apps/Iaso/constants/urls.ts +++ b/hat/assets/js/apps/Iaso/constants/urls.ts @@ -37,10 +37,12 @@ const orgUnitDetailsLogsParams = paginationPathParamsWithPrefix(LOGS_PREFIX); const orgUnitDetailsFormsParams = paginationPathParamsWithPrefix(FORMS_PREFIX); export const CHANGE_REQUEST = 'changeRequest'; +export const CHANGE_REQUEST_CONFIG = 'changeRequestConfig'; export const CONFIGURATION = 'configuration'; const ORG_UNITS = 'orgunits'; const ORG_UNITS_CHANGE_REQUEST = `${ORG_UNITS}/${CHANGE_REQUEST}`; -const ORG_UNITS_CONFIGURATION_CHANGE_REQUESTS = `${ORG_UNITS_CHANGE_REQUEST}/${CONFIGURATION}`; +const ORG_UNITS_CHANGE_REQUEST_CONFIG = `${ORG_UNITS}/${CHANGE_REQUEST_CONFIG}`; +const ORG_UNITS_CONFIGURATION_CHANGE_REQUESTS = `${ORG_UNITS_CHANGE_REQUEST_CONFIG}/${CONFIGURATION}`; // TODO export to blsq-comp export type RouteConfig = { diff --git a/hat/assets/js/apps/Iaso/domains/app/translations/en.json b/hat/assets/js/apps/Iaso/domains/app/translations/en.json index 0a632d69a3..e306f59750 100644 --- a/hat/assets/js/apps/Iaso/domains/app/translations/en.json +++ b/hat/assets/js/apps/Iaso/domains/app/translations/en.json @@ -589,6 +589,7 @@ "iaso.label.name": "Name", "iaso.label.needsAuthentication": "Authentification required", "iaso.label.newOrgUnit": "New org. unit", + "iaso.label.next": "Next", "iaso.label.no": "No", "iaso.label.noDifference": "No difference", "iaso.label.noGeographicalData": "Without geography", @@ -894,6 +895,14 @@ "iaso.orgUnitsTypes.projects": "Projects", "iaso.orgUnitsTypes.subTypesErrors": "A sub org unit type cannot be a parent too ({typeName})", "iaso.orgUnitsTypes.update": "Update org unit type", + "iaso.oucrc.closedDate": "Closing Date", + "iaso.oucrc.editableReferenceFormIds": "Editable Reference Forms", + "iaso.oucrc.groupSetIds": "Group Sets", + "iaso.oucrc.orgUnitsEditable": "Should OrgUnits of this type be editable?", + "iaso.oucrc.otherGroupIds": "Other Groups", + "iaso.oucrc.oucrcCreateModalTitle": "OrgUnit Change Request Configuration - Creation", + "iaso.oucrc.possibleParentTypeIds": "Possible New Parent Types", + "iaso.oucrc.possibleTypeIds": "Possible New Types", "iaso.page.deleteError": "Error removing embedded link", "iaso.page.deleteSuccess": "Embedded link successfully removed", "iaso.page.viewpages": "View embedded link. {linebreak} New tab: ctrl + click", diff --git a/hat/assets/js/apps/Iaso/domains/app/translations/fr.json b/hat/assets/js/apps/Iaso/domains/app/translations/fr.json index ed7f5bbed3..a6500e318d 100644 --- a/hat/assets/js/apps/Iaso/domains/app/translations/fr.json +++ b/hat/assets/js/apps/Iaso/domains/app/translations/fr.json @@ -589,6 +589,7 @@ "iaso.label.name": "Nom", "iaso.label.needsAuthentication": "Authentification requise", "iaso.label.newOrgUnit": "Nouvelle unité d'org.", + "iaso.label.next": "Suivant", "iaso.label.no": "Non", "iaso.label.noDifference": "Aucune différence", "iaso.label.noGeographicalData": "Sans données géographiques", @@ -894,6 +895,14 @@ "iaso.orgUnitsTypes.projects": "Projets", "iaso.orgUnitsTypes.subTypesErrors": "Un sous type d'unité d'organisation ne peut pas aussi être parent ({typeName})", "iaso.orgUnitsTypes.update": "Mettre à jour le type d'unité d'organisation", + "iaso.oucrc.closedDate": "Date de fermeture", + "iaso.oucrc.editableReferenceFormIds": "Formulaires de référence modifiables", + "iaso.oucrc.groupSetIds": "Ensembles de groupes", + "iaso.oucrc.orgUnitsEditable": "Les unités d'organisation de ce type doivent-elles être modifiables ?", + "iaso.oucrc.otherGroupIds": "Autres groupes", + "iaso.oucrc.oucrcCreateModalTitle": "Configuration de la demande de changement d'unité d'organisation - Création", + "iaso.oucrc.possibleParentTypeIds": "Types de parents possibles", + "iaso.oucrc.possibleTypeIds": "Nouveaux types possibles", "iaso.page.deleteError": "Erreur lors de la suppression du lien intégré", "iaso.page.deleteSuccess": "Lien intégré supprimée", "iaso.page.viewpages": "Voir le lien intégré. {linebreak} Nouvel onglet: ctrl + click", diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts index c4e8343abe..4ff982ab09 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts @@ -70,7 +70,7 @@ const MESSAGES = defineMessages({ defaultMessage: 'Next', }, oucrcCreateModalTitle: { - id: 'iaso.label.oucrcCreateModalTitle', + id: 'iaso.oucrc.oucrcCreateModalTitle', defaultMessage: 'OrgUnit Change Request Configuration - Creation', }, oucrcCreateSecondStepModalTitle: { @@ -87,15 +87,15 @@ const MESSAGES = defineMessages({ defaultMessage: 'This field is required', }, orgUnitsEditable: { - id: 'iaso.label.orgUnitsEditable', + id: 'iaso.ourcrc.orgUnitsEditable', defaultMessage: 'Should OrgUnits of this type be editable?', }, orgUnitsEditableYes: { - id: 'iaso.label.orgUnitsEditableYes', + id: 'iaso.forms.yes', defaultMessage: 'Yes', }, orgUnitsEditableNo: { - id: 'iaso.label.orgUnitsEditableNo', + id: 'iaso.forms.no', defaultMessage: 'No', }, name: { @@ -103,15 +103,15 @@ const MESSAGES = defineMessages({ defaultMessage: 'Name', }, aliases: { - id: 'iaso.label.aliases', + id: 'iaso.forms.aliases', defaultMessage: 'Aliases', }, openingDate: { - id: 'iaso.label.openingDate', + id: 'iaso.orgUnits.openingDate', defaultMessage: 'Opening Date', }, closedDate: { - id: 'iaso.label.closedDate', + id: 'iaso.oucrc.closedDate', defaultMessage: 'Closing Date', }, location: { @@ -123,19 +123,19 @@ const MESSAGES = defineMessages({ defaultMessage: 'Editable Fields', }, possibleTypeIds: { - id: 'iaso.label.possibleTypeIds', + id: 'iaso.oucrc.possibleTypeIds', defaultMessage: 'Possible New Types', }, possibleParentTypeIds: { - id: 'iaso.label.possibleParentTypeIds', + id: 'iaso.oucrc.possibleParentTypeIds', defaultMessage: 'Possible New Parent Types', }, groupSetIds: { - id: 'iaso.label.groupSetIds', + id: 'iaso.oucrc.groupSetIds', defaultMessage: 'Group Sets', }, editableReferenceFormIds: { - id: 'iaso.label.editableReferenceFormIds', + id: 'iaso.oucrc.editableReferenceFormIds', defaultMessage: 'Editable Reference Forms', }, otherGroupIds: { @@ -143,11 +143,11 @@ const MESSAGES = defineMessages({ defaultMessage: 'Other Groups', }, oucrcModalCreateButton: { - id: 'iaso.label.oucrcModalCreateButton', + id: 'iaso.label.create', defaultMessage: 'Create', }, oucrcModalUpdateButton: { - id: 'iaso.label.oucrcModalUpdateButton', + id: 'iaso.mappings.label.update', defaultMessage: 'Update', }, }); From fa49f8ae2769eda3c3a44d96b7770aa019f54ece Mon Sep 17 00:00:00 2001 From: Christophe Gerard Date: Thu, 26 Sep 2024 14:43:48 +0200 Subject: [PATCH 10/22] translations issues --- .../js/apps/Iaso/domains/app/translations/en.json | 2 ++ .../js/apps/Iaso/domains/app/translations/fr.json | 4 +++- .../Iaso/domains/orgUnits/configuration/messages.ts | 10 +++++----- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/hat/assets/js/apps/Iaso/domains/app/translations/en.json b/hat/assets/js/apps/Iaso/domains/app/translations/en.json index e306f59750..f88154a65c 100644 --- a/hat/assets/js/apps/Iaso/domains/app/translations/en.json +++ b/hat/assets/js/apps/Iaso/domains/app/translations/en.json @@ -901,6 +901,8 @@ "iaso.oucrc.orgUnitsEditable": "Should OrgUnits of this type be editable?", "iaso.oucrc.otherGroupIds": "Other Groups", "iaso.oucrc.oucrcCreateModalTitle": "OrgUnit Change Request Configuration - Creation", + "iaso.oucrc.oucrcCreateModalTitle2": "OrgUnit Change Request Configuration - Creation 2nd step", + "iaso.oucrc.oucrcCreateUpdateModalTitle": "OrgUnit Change Request Configuration - Update", "iaso.oucrc.possibleParentTypeIds": "Possible New Parent Types", "iaso.oucrc.possibleTypeIds": "Possible New Types", "iaso.page.deleteError": "Error removing embedded link", diff --git a/hat/assets/js/apps/Iaso/domains/app/translations/fr.json b/hat/assets/js/apps/Iaso/domains/app/translations/fr.json index a6500e318d..63ad0b47af 100644 --- a/hat/assets/js/apps/Iaso/domains/app/translations/fr.json +++ b/hat/assets/js/apps/Iaso/domains/app/translations/fr.json @@ -900,7 +900,9 @@ "iaso.oucrc.groupSetIds": "Ensembles de groupes", "iaso.oucrc.orgUnitsEditable": "Les unités d'organisation de ce type doivent-elles être modifiables ?", "iaso.oucrc.otherGroupIds": "Autres groupes", - "iaso.oucrc.oucrcCreateModalTitle": "Configuration de la demande de changement d'unité d'organisation - Création", + "iaso.oucrc.oucrcCreateModalTitle": "Configuration de demande de changement d'unité d'organisation - Création", + "iaso.oucrc.oucrcCreateModalTitle2": "Configuration de demande de changement d'unité d'organisation - Deuxième étape de création", + "iaso.oucrc.oucrcCreateUpdateModalTitle": "Configuration de demande de changement d'unité d'organisation - Mise à jour", "iaso.oucrc.possibleParentTypeIds": "Types de parents possibles", "iaso.oucrc.possibleTypeIds": "Nouveaux types possibles", "iaso.page.deleteError": "Erreur lors de la suppression du lien intégré", diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts index 4ff982ab09..25531bf043 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/messages.ts @@ -74,12 +74,12 @@ const MESSAGES = defineMessages({ defaultMessage: 'OrgUnit Change Request Configuration - Creation', }, oucrcCreateSecondStepModalTitle: { - id: 'iaso.label.oucrcCreateModalTitle', + id: 'iaso.oucrc.oucrcCreateModalTitle2', defaultMessage: 'OrgUnit Change Request Configuration - Creation 2nd step', }, oucrcCreateUpdateModalTitle: { - id: 'iaso.label.oucrcCreateModalTitle', + id: 'iaso.oucrc.oucrcCreateUpdateModalTitle', defaultMessage: 'OrgUnit Change Request Configuration - Update', }, requiredField: { @@ -87,7 +87,7 @@ const MESSAGES = defineMessages({ defaultMessage: 'This field is required', }, orgUnitsEditable: { - id: 'iaso.ourcrc.orgUnitsEditable', + id: 'iaso.oucrc.orgUnitsEditable', defaultMessage: 'Should OrgUnits of this type be editable?', }, orgUnitsEditableYes: { @@ -107,7 +107,7 @@ const MESSAGES = defineMessages({ defaultMessage: 'Aliases', }, openingDate: { - id: 'iaso.orgUnits.openingDate', + id: 'iaso.changeRequest.openingDate', defaultMessage: 'Opening Date', }, closedDate: { @@ -139,7 +139,7 @@ const MESSAGES = defineMessages({ defaultMessage: 'Editable Reference Forms', }, otherGroupIds: { - id: 'iaso.label.otherGroupIds', + id: 'iaso.oucrc.otherGroupIds', defaultMessage: 'Other Groups', }, oucrcModalCreateButton: { From fff00209e5078a678be3a16c52e2f92308e419ce Mon Sep 17 00:00:00 2001 From: Christophe Gerard Date: Thu, 26 Sep 2024 14:49:25 +0200 Subject: [PATCH 11/22] fix delete --- .../hooks/api/useDeleteOrgUnitChangeRequestConfig.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useDeleteOrgUnitChangeRequestConfig.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useDeleteOrgUnitChangeRequestConfig.ts index 37fc6f1daf..9d06e02b55 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useDeleteOrgUnitChangeRequestConfig.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useDeleteOrgUnitChangeRequestConfig.ts @@ -1,14 +1,14 @@ import { UseMutationResult } from 'react-query'; import { useSnackMutation } from '../../../../../libs/apiHooks'; +import { deleteRequest } from '../../../../../libs/Api'; import { apiUrlOUCRC } from '../../constants'; import { OrgUnitChangeRequestConfigurationFull } from '../../types'; -import { deleteRequest } from '../../../../../libs/Api'; const deleteOrgUnitChangeRequestConfigs = ( config: OrgUnitChangeRequestConfigurationFull, ) => { - return deleteRequest(`${apiUrlOUCRC}/${config.id}/`) as Promise; + return deleteRequest(`${apiUrlOUCRC}${config.id}/`) as Promise; }; export const useDeleteOrgUnitChangeRequestConfig = (): UseMutationResult => From 0bdc9d0e2d657c7a5797d456d27c37a7be3c5b57 Mon Sep 17 00:00:00 2001 From: Christophe Gerard Date: Thu, 26 Sep 2024 16:00:42 +0200 Subject: [PATCH 12/22] get group sets --- .../Iaso/domains/app/translations/en.json | 7 +-- .../Iaso/domains/app/translations/fr.json | 1 + .../OrgUnitChangeRequestConfigDialog.tsx | 6 +-- .../hooks/requests/useGetGroupSets.ts | 33 +++++++++++++ .../js/apps/Iaso/domains/orgUnits/messages.ts | 4 ++ iaso/api/group_sets/views.py | 48 +++++++++++++++++-- 6 files changed, 88 insertions(+), 11 deletions(-) create mode 100644 hat/assets/js/apps/Iaso/domains/orgUnits/hooks/requests/useGetGroupSets.ts diff --git a/hat/assets/js/apps/Iaso/domains/app/translations/en.json b/hat/assets/js/apps/Iaso/domains/app/translations/en.json index 8f77ce9f78..7642ff5d74 100644 --- a/hat/assets/js/apps/Iaso/domains/app/translations/en.json +++ b/hat/assets/js/apps/Iaso/domains/app/translations/en.json @@ -320,8 +320,8 @@ "iaso.groups.sourceVersion": "Source version", "iaso.groups.update": "Update group", "iaso.groupsets.dialog.delete": "Are you sure you want to delete this groupset?", - "iaso.groupsets.dialog.deleteText":"This operation cannot be undone.", - "iaso.groupsets.groupBelonging": "Groups belonging", + "iaso.groupsets.dialog.deleteText": "This operation cannot be undone.", + "iaso.groupsets.groupBelonging": "Groups belonging", "iaso.groupsets.validation.field_required": "Ce champ est obligatoire", "iaso.hospital": "Hospital", "iaso.instance.coordinate": "Coordinates", @@ -1190,6 +1190,7 @@ "iaso.snackBar.fetchFormsError": "An error occurred while fetching forms list", "iaso.snackBar.fetchFormVersionsError": "An error occurred while fetching form versions", "iaso.snackBar.fetchGroupsError": "An error occurred while fetching groups list", + "iaso.snackBar.fetchGroupSetsError": "An error occurred while fetching group sets list", "iaso.snackBar.fetchingLogDetailError": "An error occurred while fetching log details", "iaso.snackBar.fetchInstanceDictError": "An error occurred while fetching instances list", "iaso.snackBar.fetchInstanceError": "An error occurred while fetching instance detail", @@ -1509,4 +1510,4 @@ "trypelim.permissions.zones": "Zones", "trypelim.permissions.zones_edit": "Edit zones", "trypelim.permissions.zones_shapes_edit": "Edit zone shapes" -} +} \ No newline at end of file diff --git a/hat/assets/js/apps/Iaso/domains/app/translations/fr.json b/hat/assets/js/apps/Iaso/domains/app/translations/fr.json index 0d895a82c2..597a89aa15 100644 --- a/hat/assets/js/apps/Iaso/domains/app/translations/fr.json +++ b/hat/assets/js/apps/Iaso/domains/app/translations/fr.json @@ -1190,6 +1190,7 @@ "iaso.snackBar.fetchFormsError": "Une erreur est survenue en récupérant la liste des formulaires", "iaso.snackBar.fetchFormVersionsError": "Une erreur est survenue en récupérant les version du formulaire", "iaso.snackBar.fetchGroupsError": "Une erreur est survenue en récupérant la liste des groupes", + "iaso.snackBar.fetchGroupSetsError": "Une erreur est survenue en récupérant la liste des group sets", "iaso.snackBar.fetchingLogDetailError": "Une erreur est survenu en récupérant l'historique", "iaso.snackBar.fetchInstanceDictError": "Une erreur est survenue en récupérant la liste des soumissions", "iaso.snackBar.fetchInstanceError": "Une erreur est survenue en récupérant le detail de la soumission", diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx index 4be0d6a191..29a399c7b0 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx @@ -12,6 +12,7 @@ import { EditIconButton } from '../../../../components/Buttons/EditIconButton'; import InputComponent from '../../../../components/forms/InputComponent'; import { useTranslatedErrors } from '../../../../libs/validation'; import { useGetGroupDropdown } from '../../hooks/requests/useGetGroups'; +import { useGetGroupSetsDropdown } from '../../hooks/requests/useGetGroupSets'; import { useGetOrgUnitTypesDropdownOptions } from '../../orgUnitTypes/hooks/useGetOrgUnitTypesDropdownOptions'; import { editableFieldsManyToManyFields } from '../constants'; import { useGetFormDropdownOptions } from '../hooks/api/useGetFormDropdownOptions'; @@ -81,7 +82,7 @@ const OrgUnitChangeRequestConfigDialog: FunctionComponent = ({ const { data: formOptions } = useGetFormDropdownOptions( config.orgUnitType.id, ); - const { data: groupSetOptions } = useGetGroupDropdown({}); + const { data: groupSetOptions } = useGetGroupSetsDropdown(); const { mutateAsync: saveConfig } = useSaveOrgUnitChangeRequestConfiguration(); const orgUnitsEditableOptions = useOrgUnitsEditableOptions(); @@ -135,7 +136,6 @@ const OrgUnitChangeRequestConfigDialog: FunctionComponent = ({ ); const allowConfirm = isValid && !isSubmitting && !isEqual(touched, {}); - return ( = ({ value={values.groupSetIds} errors={getErrors('groupSetIds')} label={MESSAGES.groupSetIds} - options={groupSetOptions} // Warning: no call for groupsets ATM (using groups as placeholder) + options={groupSetOptions} /> )} {values?.editableFields && diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/hooks/requests/useGetGroupSets.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/hooks/requests/useGetGroupSets.ts new file mode 100644 index 0000000000..30098799c4 --- /dev/null +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/hooks/requests/useGetGroupSets.ts @@ -0,0 +1,33 @@ +import { UseQueryResult } from 'react-query'; +// @ts-ignore +import { useSnackQuery } from 'Iaso/libs/apiHooks.ts'; +// @ts-ignore +import { getRequest } from 'Iaso/libs/Api'; +import { DropdownOptions } from '../../../../types/utils'; + +import { staleTime } from '../../config'; +import MESSAGES from '../../messages'; + +export const useGetGroupSetsDropdown = (): UseQueryResult< + DropdownOptions[], + Error +> => { + return useSnackQuery({ + queryKey: ['groupSets'], + queryFn: () => getRequest(`/api/group_sets/dropdown/`), + snackErrorMsg: MESSAGES.fetchGroupSetsError, + options: { + staleTime, + select: data => { + if (!data) return []; + return data.map(groupSet => { + return { + value: groupSet.id, + label: groupSet.name, + original: groupSet, + }; + }); + }, + }, + }); +}; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/messages.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/messages.ts index 1645bdad5f..4220a87c78 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/messages.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/messages.ts @@ -382,6 +382,10 @@ const MESSAGES = defineMessages({ id: 'iaso.snackBar.fetchGroupsError', defaultMessage: 'An error occurred while fetching groups list', }, + fetchGroupSetsError: { + id: 'iaso.snackBar.fetchGroupSetsError', + defaultMessage: 'An error occurred while fetching group sets list', + }, fetchProfilesError: { id: 'iaso.snackBar.fetchProfilesError', defaultMessage: 'An error occurred while fetching profiles list', diff --git a/iaso/api/group_sets/views.py b/iaso/api/group_sets/views.py index 4f971df976..3ddafeae42 100644 --- a/iaso/api/group_sets/views.py +++ b/iaso/api/group_sets/views.py @@ -1,13 +1,15 @@ import django_filters -from rest_framework import permissions +from rest_framework import filters, permissions, serializers, status +from rest_framework.decorators import action from rest_framework.response import Response -from rest_framework import filters, status -from iaso.models import GroupSet -from ..common import ModelViewSet, HasPermission + from hat.menupermissions import models as permission from iaso.api.common import Paginator -from .serializers import GroupSetSerializer +from iaso.models import GroupSet, Project, SourceVersion + +from ..common import HasPermission, ModelViewSet from .filters import GroupSetFilter +from .serializers import GroupSetSerializer class HasGroupsetPermission(permissions.BasePermission): @@ -26,6 +28,13 @@ class GroupSetPagination(Paginator): page_size = 10 +class GroupSetDropdownSerializer(serializers.ModelSerializer): + class Meta: + model = GroupSet + fields = ["id", "name"] + read_only_fields = ["id", "name"] + + class GroupSetsViewSet(ModelViewSet): f"""Groups API @@ -75,3 +84,32 @@ def create(self, request, *args, **kwargs): serializer.save() headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + + @action(permission_classes=[], detail=False, methods=["GET"], serializer_class=GroupSetDropdownSerializer) + def dropdown(self, request, *args): + """To be used in dropdowns (filters) + + * Read only + * Readable anonymously if feature flag on project allow them and an app_id parameter is passed + * No permission needed + """ + + app_id = self.request.query_params.get("app_id") + user = request.user + if user and user.is_anonymous and app_id is None: + raise serializers.ValidationError("Parameter app_id is missing") + + if user and user.is_authenticated: + account = user.iaso_profile.account + # Filter on version ids (linked to the account) + versions = SourceVersion.objects.filter(data_source__projects__account=account) + + else: + # this check if project need auth + project = Project.objects.get_for_user_and_app_id(user, app_id) + versions = SourceVersion.objects.filter(data_source__projects=project) + group_sets = GroupSet.objects.filter(source_version__in=versions).distinct() + + queryset = self.filter_queryset(group_sets) + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) From ee18560925cf4807b1578560f03d20fb6c8f0385 Mon Sep 17 00:00:00 2001 From: Christophe Gerard Date: Fri, 27 Sep 2024 09:52:00 +0200 Subject: [PATCH 13/22] align with benjamin --- .../configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx | 4 ++++ .../changesConfiguration/changeRequestConfiguration.spec.js | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx index 29a399c7b0..00554ec66e 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx @@ -32,7 +32,11 @@ type Props = { isOpen: boolean; closeDialog: () => void; }; +// rajouter les champs dans forms editable et rendre possible_type_ids, possible_parent_type_ids, editable_reference_form_ids, other_group_ids to non required +// Laisser toujours group sets et l'enlever de editable fields +// NAME, ALIASES, ORG_UNIT_TYPE, OPENING_DATE, +// CLOSING_DATE, LOCATION, PARENT_TYPE, REFERENCE_FORMS, OTHER_GROUPS; const OrgUnitChangeRequestConfigDialog: FunctionComponent = ({ config, isOpen, diff --git a/hat/assets/js/cypress/integration/05 - orgUnits/changesConfiguration/changeRequestConfiguration.spec.js b/hat/assets/js/cypress/integration/05 - orgUnits/changesConfiguration/changeRequestConfiguration.spec.js index 043a9cddfb..f58adc0119 100644 --- a/hat/assets/js/cypress/integration/05 - orgUnits/changesConfiguration/changeRequestConfiguration.spec.js +++ b/hat/assets/js/cypress/integration/05 - orgUnits/changesConfiguration/changeRequestConfiguration.spec.js @@ -4,15 +4,15 @@ import emptyFixture from '../../../fixtures/orgunits/changes/configuration/empty import page2 from '../../../fixtures/orgunits/changes/configuration/orgUnitChangeConfigurations-page2.json'; import listFixture from '../../../fixtures/orgunits/changes/configuration/orgUnitChangeConfigurations.json'; import orgUnitTypesFixture from '../../../fixtures/orgunittypes/list.json'; -import projectsFixture from '../../../fixtures/projects/list.json'; import superUser from '../../../fixtures/profiles/me/superuser.json'; +import projectsFixture from '../../../fixtures/projects/list.json'; import { testPageFilters } from '../../../support/testPageFilters'; import { testPagination } from '../../../support/testPagination'; import { testTablerender } from '../../../support/testTableRender'; import { testTableSort } from '../../../support/testTableSort'; const siteBaseUrl = Cypress.env('siteBaseUrl'); -const baseUrl = `${siteBaseUrl}/dashboard/orgunits/configuration/changeRequest`; +const baseUrl = `${siteBaseUrl}/dashboard/orgunits/changeRequestConfig/configuration`; let interceptFlag = false; const defaultQuery = { From afcb07456456f8261200281aedfd2a5b7496cce3 Mon Sep 17 00:00:00 2001 From: Thibault Dethier Date: Fri, 27 Sep 2024 11:58:56 +0200 Subject: [PATCH 14/22] [IA-3443] Update editable_fields - WIP --- .../OrgUnitChangeRequestConfigDialog.tsx | 26 ++++++++--------- .../orgUnits/configuration/constants.ts | 21 +++++++------- .../useRetrieveOrgUnitChangeRequestConfig.ts | 29 +++++-------------- ...seSaveOrgUnitChangeRequestConfiguration.ts | 20 +++++-------- .../org_unit_change_request_configuration.py | 6 ++-- 5 files changed, 44 insertions(+), 58 deletions(-) diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx index 00554ec66e..4b8a95b96d 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx @@ -133,6 +133,7 @@ const OrgUnitChangeRequestConfigDialog: FunctionComponent = ({ setFieldValue(field, undefined); }); setFieldValue('editableFields', undefined); + setFieldValue('groupSetIds', undefined); } onChange(keyValue, boolValue); }, @@ -193,6 +194,18 @@ const OrgUnitChangeRequestConfigDialog: FunctionComponent = ({ options={orgUnitEditableFieldsOptions} /> )} + {values?.orgUnitsEditable && ( + + )} {values?.editableFields && values.editableFields.includes('possibleTypeIds') && ( = ({ options={orgUnitTypeOptions} // Warning: we should probably filter data here (only what is available in parent/child relationship) /> )} - {values?.editableFields && - values.editableFields.includes('groupSetIds') && ( - - )} {values?.editableFields && values.editableFields.includes('editableReferenceFormIds') && ( { return getRequest(url) as Promise; @@ -15,32 +12,22 @@ const retrieveOrgUnitChangeRequestConfig = (url: string) => { const fieldMapping: { [key: string]: string } = { opening_date: 'openingDate', closed_date: 'closedDate', + possible_types: 'possibleTypeIds', + possible_parent_types: 'possibleParentTypeIds', + editable_reference_forms: 'editableReferenceFormIds', + other_groups: 'otherGroupIds', }; -const conditionalFields = [ - { key: 'possible_types', name: 'possibleTypeIds' }, - { key: 'possible_parent_types', name: 'possibleParentTypeIds' }, - { key: 'group_sets', name: 'groupSetIds' }, - { key: 'editable_reference_forms', name: 'editableReferenceFormIds' }, - { key: 'other_groups', name: 'otherGroupIds' }, -]; - export const computeEditableFields = ( data: OrgUnitChangeRequestConfigurationFull, ): string[] => { - const editableFields: string[] = (data.editable_fields || []).map(field => { + return (data.editable_fields || []).map(field => { return fieldMapping[field] || field; }); - - conditionalFields.forEach(({ key, name }) => { - if ((data[key] ?? []).length > 0) { - editableFields.push(name); - } - }); - - return editableFields; }; + const mapAndJoin = (items: any[] = []) => items?.map(item => item.id).join(','); + export const useRetrieveOrgUnitChangeRequestConfig = ( configId?: number, ): UseQueryResult => { diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts index 8d6090f595..38c72b969e 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts @@ -2,21 +2,16 @@ import { UseMutationResult } from 'react-query'; import { patchRequest, postRequest } from '../../../../../libs/Api'; import { useSnackMutation } from '../../../../../libs/apiHooks'; -import { apiUrlOUCRC, editableFieldsForBackend } from '../../constants'; +import { apiUrlOUCRC, mappingEditableFieldsForBackend } from '../../constants'; import { OrgUnitChangeRequestConfigurationForm } from '../../types'; const cleanEditableFieldsForSaving = (editableFields?: string): string[] => { if (!editableFields) { return []; } - return editableFields - .split(',') - .filter(field => editableFieldsForBackend.includes(field)) - .map(field => { - if (field === 'openingDate') return 'opening_date'; - if (field === 'closedDate') return 'closed_date'; - return field; - }); + return editableFields.split(',').map(field => { + return mappingEditableFieldsForBackend[field]; + }); }; type ApiValues = { @@ -39,19 +34,20 @@ const mapValuesForSaving = ( org_units_editable: values.orgUnitsEditable ?? false, }; // These two fields can't be updated so they are only set for creation - if (configId) { + if (!configId) { apiValues.project_id = values.projectId; apiValues.org_unit_type_id = values.orgUnitTypeId; } // This field must be cleaned because the backend accepts only some values - apiValues.editable_fields = cleanEditableFieldsForSaving( values.editableFields, ); // All the many to many fields are added if they have a value - const splitAndMapToNumbers = (str?: string) => str?.split(',').map(Number); + const splitAndMapToNumbers = (str?: string) => { + return str?.trim() ? str.split(',').map(Number) : []; + }; apiValues.possible_type_ids = splitAndMapToNumbers(values.possibleTypeIds); apiValues.possible_parent_type_ids = splitAndMapToNumbers( diff --git a/iaso/models/org_unit_change_request_configuration.py b/iaso/models/org_unit_change_request_configuration.py index 4f5768fbe2..4a6a0908a1 100644 --- a/iaso/models/org_unit_change_request_configuration.py +++ b/iaso/models/org_unit_change_request_configuration.py @@ -77,14 +77,16 @@ class OrgUnitChangeRequestConfiguration(SoftDeletableModel): OrgUnitChangeRequestConfigurationQuerySet )() - # Only the non-relationship fields since an ID present in any relationship - # means that the field is editable by the user LIST_OF_POSSIBLE_EDITABLE_FIELDS = [ "name", "aliases", "opening_date", "closed_date", "location", + "possible_types", + "possible_parent_types", + "editable_reference_forms", + "other_groups", ] # Used to easily create/update objects in serializers From 8dd775e78835d33c76468954ed3897b5cdbe8b40 Mon Sep 17 00:00:00 2001 From: Christophe Gerard Date: Fri, 27 Sep 2024 14:51:20 +0200 Subject: [PATCH 15/22] fix validation, yup typing for custom command --- .../OrgUnitChangeRequestConfigDialog.tsx | 8 ++--- .../useDeleteOrgUnitChangeRequestConfig.ts | 6 +++- ...seSaveOrgUnitChangeRequestConfiguration.ts | 1 + .../hooks/useValidationSchemaOUCRC.ts | 31 ++++++++----------- .../orgUnits/hooks/requests/useGetGroups.ts | 6 ++-- hat/assets/js/apps/Iaso/types/yup.d.ts | 9 ++++++ 6 files changed, 34 insertions(+), 27 deletions(-) create mode 100644 hat/assets/js/apps/Iaso/types/yup.d.ts diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx index 4b8a95b96d..6a6682ce31 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx @@ -32,11 +32,7 @@ type Props = { isOpen: boolean; closeDialog: () => void; }; -// rajouter les champs dans forms editable et rendre possible_type_ids, possible_parent_type_ids, editable_reference_form_ids, other_group_ids to non required -// Laisser toujours group sets et l'enlever de editable fields -// NAME, ALIASES, ORG_UNIT_TYPE, OPENING_DATE, -// CLOSING_DATE, LOCATION, PARENT_TYPE, REFERENCE_FORMS, OTHER_GROUPS; const OrgUnitChangeRequestConfigDialog: FunctionComponent = ({ config, isOpen, @@ -82,7 +78,9 @@ const OrgUnitChangeRequestConfigDialog: FunctionComponent = ({ } }, [fetchedConfig, setValues]); const { data: orgUnitTypeOptions } = useGetOrgUnitTypesDropdownOptions(); - const { data: groupOptions } = useGetGroupDropdown({}); + const { data: groupOptions } = useGetGroupDropdown({ + defaultVersion: 'true', + }); const { data: formOptions } = useGetFormDropdownOptions( config.orgUnitType.id, ); diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useDeleteOrgUnitChangeRequestConfig.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useDeleteOrgUnitChangeRequestConfig.ts index 9d06e02b55..5df2bfdbdf 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useDeleteOrgUnitChangeRequestConfig.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useDeleteOrgUnitChangeRequestConfig.ts @@ -14,5 +14,9 @@ const deleteOrgUnitChangeRequestConfigs = ( export const useDeleteOrgUnitChangeRequestConfig = (): UseMutationResult => useSnackMutation({ mutationFn: deleteOrgUnitChangeRequestConfigs, - invalidateQueryKey: 'getOrgUnitChangeRequestConfigs', + invalidateQueryKey: [ + 'useRetrieveOrgUnitChangeRequestConfig', + 'getOrgUnitChangeRequestConfigs', + 'checkAvailabilityOrgUnitChangeRequestConfigs', + ], }); diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts index 38c72b969e..c66129d65f 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts @@ -96,6 +96,7 @@ export const useSaveOrgUnitChangeRequestConfiguration = invalidateQueryKey: [ 'useRetrieveOrgUnitChangeRequestConfig', 'getOrgUnitChangeRequestConfigs', + 'checkAvailabilityOrgUnitChangeRequestConfigs', ], }); }; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts index 121f9a4798..0ff1801207 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts @@ -8,10 +8,13 @@ yup.addMethod( function isMultiSelectValid(formatMessage) { return this.test('isMultiSelectValid', '', (value, context) => { const { path, createError, parent } = context; - if (!parent.editableFields) { - return true; + if (!parent.editableFields && !parent.groupSetIds) { + return createError({ + path, + message: formatMessage(MESSAGES.requiredField), + }); } - const splitFields = parent.editableFields; + const splitFields = parent.editableFields || []; const isFieldPopulated = parent[path]; if (!isFieldPopulated && splitFields.includes(path)) { return createError({ @@ -41,7 +44,7 @@ export const useValidationSchemaOUCRC = () => { .required(formatMessage(MESSAGES.requiredField)), editableFields: yup.string().nullable().when('orgUnitsEditable', { is: true, - then: yup.string().nullable().required(), + then: yup.string().nullable(), otherwise: yup.string().nullable(), }), possibleTypeIds: yup @@ -49,7 +52,6 @@ export const useValidationSchemaOUCRC = () => { .nullable() .when('orgUnitsEditable', { is: true, - // @ts-ignore then: yup.string().nullable().isMultiSelectValid(formatMessage), otherwise: yup.string().nullable(), }), @@ -58,7 +60,6 @@ export const useValidationSchemaOUCRC = () => { .nullable() .when('orgUnitsEditable', { is: true, - // @ts-ignore then: yup.string().nullable().isMultiSelectValid(formatMessage), otherwise: yup.string().nullable(), }), @@ -67,7 +68,6 @@ export const useValidationSchemaOUCRC = () => { .nullable() .when('orgUnitsEditable', { is: true, - // @ts-ignore then: yup.string().nullable().isMultiSelectValid(formatMessage), otherwise: yup.string().nullable(), }), @@ -76,18 +76,13 @@ export const useValidationSchemaOUCRC = () => { .nullable() .when('orgUnitsEditable', { is: true, - // @ts-ignore - then: yup.string().nullable().isMultiSelectValid(formatMessage), - otherwise: yup.string().nullable(), - }), - otherGroupIds: yup - .string() - .nullable() - .when('orgUnitsEditable', { - is: true, - // @ts-ignore - then: yup.string().nullable().isMultiSelectValid(formatMessage), + then: yup.string().nullable(), otherwise: yup.string().nullable(), }), + otherGroupIds: yup.string().nullable().when('orgUnitsEditable', { + is: true, + then: yup.string().nullable(), + otherwise: yup.string().nullable(), + }), }); }; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/hooks/requests/useGetGroups.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/hooks/requests/useGetGroups.ts index cb2d887d8d..ff19297f42 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/hooks/requests/useGetGroups.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/hooks/requests/useGetGroups.ts @@ -5,8 +5,8 @@ import { useSnackQuery } from 'Iaso/libs/apiHooks.ts'; import { getRequest } from 'Iaso/libs/Api'; import { DropdownOptions } from '../../../../types/utils'; -import MESSAGES from '../../messages'; import { staleTime } from '../../config'; +import MESSAGES from '../../messages'; type Props = { dataSourceId?: number; @@ -25,7 +25,6 @@ const makeGroupsQueryParams = ({ return ''; }; - export const useGetGroups = ({ dataSourceId, sourceVersionId, @@ -74,6 +73,7 @@ type Params = { sourceVersionId?: number; blockOfCountries?: string; appId?: string; + defaultVersion?: string; }; export const useGetGroupDropdown = ( params: Params, @@ -83,7 +83,7 @@ export const useGetGroupDropdown = ( if (params[keyInJS]) { queryParams[keyInApi] = params[keyInJS]; } - }); + }); const urlSearchParams = new URLSearchParams(queryParams); const queryString = urlSearchParams.toString(); return useSnackQuery({ diff --git a/hat/assets/js/apps/Iaso/types/yup.d.ts b/hat/assets/js/apps/Iaso/types/yup.d.ts new file mode 100644 index 0000000000..fd68e3b2d3 --- /dev/null +++ b/hat/assets/js/apps/Iaso/types/yup.d.ts @@ -0,0 +1,9 @@ +import 'yup'; + +declare module 'yup' { + interface StringSchema { + isMultiSelectValid( + formatMessage: (message: any) => string, + ): StringSchema; + } +} From 63074681473c7441896111f978fb6166306db384 Mon Sep 17 00:00:00 2001 From: Christophe Gerard Date: Fri, 27 Sep 2024 15:59:55 +0200 Subject: [PATCH 16/22] fix cypress tests --- .../changeRequestConfiguration.spec.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hat/assets/js/cypress/integration/05 - orgUnits/changesConfiguration/changeRequestConfiguration.spec.js b/hat/assets/js/cypress/integration/05 - orgUnits/changesConfiguration/changeRequestConfiguration.spec.js index f58adc0119..1ec6c88628 100644 --- a/hat/assets/js/cypress/integration/05 - orgUnits/changesConfiguration/changeRequestConfiguration.spec.js +++ b/hat/assets/js/cypress/integration/05 - orgUnits/changesConfiguration/changeRequestConfiguration.spec.js @@ -85,7 +85,12 @@ const testRowContent = (index, config = listFixture.results[index]) => { r.colAt(2).should('contain', config.org_unit_type.name); r.colAt(3).should('contain', formatDate(config.created_at)); r.colAt(4).should('contain', formatDate(config.updated_at)); - r.colAt(5).should('contain', config.editable_fields.join(', ')); + r.colAt(5) + .find('.MuiChip-label') + .should(labels => { + const labelTexts = [...labels].map(label => label.textContent); + expect(labelTexts).to.include.members(['1', '2', '3']); + }); }); }; @@ -197,10 +202,6 @@ describe('OrgUnit Change Configuration', () => { colIndex: 4, order: 'updated_at', }, - { - colIndex: 5, - order: 'editable_fields', - }, ]; sorts.forEach(s => { testTableSort({ From 20b95e556805bacb021280c56324a5f8e94c29b5 Mon Sep 17 00:00:00 2001 From: Christophe Gerard Date: Fri, 27 Sep 2024 16:33:27 +0200 Subject: [PATCH 17/22] fix project filter --- hat/assets/js/apps/Iaso/constants/urls.ts | 10 ++++++++-- .../Filter/OrgUnitChangeRequestConfigsFilter.tsx | 12 ++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/hat/assets/js/apps/Iaso/constants/urls.ts b/hat/assets/js/apps/Iaso/constants/urls.ts index 5639a28baf..a79ffb6699 100644 --- a/hat/assets/js/apps/Iaso/constants/urls.ts +++ b/hat/assets/js/apps/Iaso/constants/urls.ts @@ -207,7 +207,7 @@ export const baseRouteConfigs: Record = { 'userIds', 'userRoles', 'withLocation', - 'projectIds', + 'project_id', 'paymentStatus', ...paginationPathParams, 'paymentIds', @@ -352,7 +352,13 @@ export const baseRouteConfigs: Record = { }, groupSets: { url: 'orgunits/groupSets', - params: ['accountId', 'search', 'sourceVersion', 'projectsIds', ...paginationPathParams], + params: [ + 'accountId', + 'search', + 'sourceVersion', + 'projectsIds', + ...paginationPathParams, + ], }, groupSetDetail: { url: 'orgunits/groupSet', diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Filter/OrgUnitChangeRequestConfigsFilter.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Filter/OrgUnitChangeRequestConfigsFilter.tsx index eb2ef4532e..23c6664efe 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Filter/OrgUnitChangeRequestConfigsFilter.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Filter/OrgUnitChangeRequestConfigsFilter.tsx @@ -1,14 +1,14 @@ -import React, { FunctionComponent } from 'react'; import { Box, Grid } from '@mui/material'; import { useSafeIntl } from 'bluesquare-components'; -import { useFilterState } from '../../../../hooks/useFilterState'; +import React, { FunctionComponent } from 'react'; +import { FilterButton } from '../../../../components/FilterButton'; import InputComponent from '../../../../components/forms/InputComponent'; import { baseUrls } from '../../../../constants/urls'; -import { useGetOrgUnitTypesDropdownOptions } from '../../orgUnitTypes/hooks/useGetOrgUnitTypesDropdownOptions'; -import { OrgUnitChangeRequestConfigsParams } from '../types'; +import { useFilterState } from '../../../../hooks/useFilterState'; import { useGetProjectsDropdownOptions } from '../../../projects/hooks/requests'; +import { useGetOrgUnitTypesDropdownOptions } from '../../orgUnitTypes/hooks/useGetOrgUnitTypesDropdownOptions'; import MESSAGES from '../messages'; -import { FilterButton } from '../../../../components/FilterButton'; +import { OrgUnitChangeRequestConfigsParams } from '../types'; const baseUrl = baseUrls.orgUnitsChangeRequestConfiguration; type Props = { params: OrgUnitChangeRequestConfigsParams }; @@ -30,7 +30,7 @@ export const OrgUnitChangeRequestConfigsFilter: FunctionComponent = ({ Date: Fri, 27 Sep 2024 16:36:14 +0200 Subject: [PATCH 18/22] remove unused params --- hat/assets/js/apps/Iaso/constants/urls.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/hat/assets/js/apps/Iaso/constants/urls.ts b/hat/assets/js/apps/Iaso/constants/urls.ts index a79ffb6699..d37a0c76cc 100644 --- a/hat/assets/js/apps/Iaso/constants/urls.ts +++ b/hat/assets/js/apps/Iaso/constants/urls.ts @@ -197,21 +197,9 @@ export const baseRouteConfigs: Record = { url: ORG_UNITS_CONFIGURATION_CHANGE_REQUESTS, params: [ 'accountId', - 'parent_id', - 'groups', 'org_unit_type_id', - 'status', - 'created_at_after', - 'created_at_before', - 'forms', - 'userIds', - 'userRoles', - 'withLocation', 'project_id', - 'paymentStatus', ...paginationPathParams, - 'paymentIds', - 'potentialPaymentIds', ], }, registry: { From be1007e8486649d2fd20e5bc0db8d6f4ff117143 Mon Sep 17 00:00:00 2001 From: Christophe Gerard Date: Mon, 30 Sep 2024 14:57:38 +0200 Subject: [PATCH 19/22] rename editable fields --- .../OrgUnitChangeRequestConfigDialog.tsx | 4 +++- .../orgUnits/configuration/constants.ts | 18 +++++++++--------- .../useRetrieveOrgUnitChangeRequestConfig.ts | 11 +++++++---- .../org_unit_change_request_configuration.py | 13 +++++++------ 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx index 6a6682ce31..80bfd1728e 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx @@ -33,6 +33,9 @@ type Props = { closeDialog: () => void; }; +// we should filter forms, groups, groupsets, types using correct project id +/// marquer les types comme requis + const OrgUnitChangeRequestConfigDialog: FunctionComponent = ({ config, isOpen, @@ -89,7 +92,6 @@ const OrgUnitChangeRequestConfigDialog: FunctionComponent = ({ useSaveOrgUnitChangeRequestConfiguration(); const orgUnitsEditableOptions = useOrgUnitsEditableOptions(); const orgUnitEditableFieldsOptions = useOrgUnitsEditableFieldsOptions(); - const { formatMessage } = useSafeIntl(); const getErrors = useTranslatedErrors({ errors, diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/constants.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/constants.ts index f9db30c476..eda4c27b07 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/constants.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/constants.ts @@ -13,16 +13,16 @@ export const editableFields = [ 'otherGroupIds', ]; export const mappingEditableFieldsForBackend = { - 'name': 'name', + name: 'name', // 'aliases': 'aliases', commented out because right now the feature is not ready yet - 'openingDate': 'opening_date', - 'closedDate': 'closed_date', - 'location': 'location', - 'possibleTypeIds': 'possible_types', - 'possibleParentTypeIds': 'possible_parent_types', - 'editableReferenceFormIds': 'editable_reference_forms', - 'otherGroupIds': 'other_groups', -} + openingDate: 'opening_date', + closedDate: 'closing_date', + location: 'location', + possibleTypeIds: 'org_unit_type', + possibleParentTypeIds: 'parent_type', + editableReferenceFormIds: 'editable_reference_forms', + otherGroupIds: 'other_groups', +}; export const editableFieldsManyToManyFields = [ 'possibleTypeIds', 'possibleParentTypeIds', diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts index cc54a756d6..eacaf808e0 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useRetrieveOrgUnitChangeRequestConfig.ts @@ -3,7 +3,10 @@ import { getRequest } from '../../../../../libs/Api'; import { useSnackQuery } from '../../../../../libs/apiHooks'; import { apiUrlOUCRC } from '../../constants'; -import { OrgUnitChangeRequestConfigurationForm, OrgUnitChangeRequestConfigurationFull } from '../../types'; +import { + OrgUnitChangeRequestConfigurationForm, + OrgUnitChangeRequestConfigurationFull, +} from '../../types'; const retrieveOrgUnitChangeRequestConfig = (url: string) => { return getRequest(url) as Promise; @@ -11,9 +14,9 @@ const retrieveOrgUnitChangeRequestConfig = (url: string) => { const fieldMapping: { [key: string]: string } = { opening_date: 'openingDate', - closed_date: 'closedDate', - possible_types: 'possibleTypeIds', - possible_parent_types: 'possibleParentTypeIds', + closing_date: 'closedDate', + org_unit_type: 'possibleTypeIds', + parent_type: 'possibleParentTypeIds', editable_reference_forms: 'editableReferenceFormIds', other_groups: 'otherGroupIds', }; diff --git a/iaso/models/org_unit_change_request_configuration.py b/iaso/models/org_unit_change_request_configuration.py index 4a6a0908a1..2c0cab5934 100644 --- a/iaso/models/org_unit_change_request_configuration.py +++ b/iaso/models/org_unit_change_request_configuration.py @@ -1,5 +1,6 @@ import typing -from django.contrib.auth.models import User, AnonymousUser + +from django.contrib.auth.models import AnonymousUser, User from django.contrib.postgres.fields import ArrayField from django.core.exceptions import ValidationError from django.db import models @@ -8,10 +9,10 @@ from iaso.models.entity import UserNotAuthError from iaso.utils.models.soft_deletable import ( - SoftDeletableModel, DefaultSoftDeletableManager, - OnlyDeletedSoftDeletableManager, IncludeDeletedSoftDeletableManager, + OnlyDeletedSoftDeletableManager, + SoftDeletableModel, ) @@ -81,10 +82,10 @@ class OrgUnitChangeRequestConfiguration(SoftDeletableModel): "name", "aliases", "opening_date", - "closed_date", + "closing_date", "location", - "possible_types", - "possible_parent_types", + "org_unit_type", + "parent_type", "editable_reference_forms", "other_groups", ] From 96c1076996a0ccb34b1ac250aaaec6e46287c0ed Mon Sep 17 00:00:00 2001 From: Christophe Gerard Date: Mon, 30 Sep 2024 14:58:09 +0200 Subject: [PATCH 20/22] filtering types by project --- .../OrgUnitChangeRequestConfigDialog.tsx | 5 +- .../useGetOrgUnitTypesDropdownOptions.ts | 60 +++++++++++-------- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx index 80bfd1728e..3f33310d22 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx @@ -80,12 +80,15 @@ const OrgUnitChangeRequestConfigDialog: FunctionComponent = ({ setValues(fetchedConfig); } }, [fetchedConfig, setValues]); - const { data: orgUnitTypeOptions } = useGetOrgUnitTypesDropdownOptions(); + const { data: orgUnitTypeOptions } = useGetOrgUnitTypesDropdownOptions( + config.project.id, + ); const { data: groupOptions } = useGetGroupDropdown({ defaultVersion: 'true', }); const { data: formOptions } = useGetFormDropdownOptions( config.orgUnitType.id, + config.project.id, ); const { data: groupSetOptions } = useGetGroupSetsDropdown(); const { mutateAsync: saveConfig } = diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/orgUnitTypes/hooks/useGetOrgUnitTypesDropdownOptions.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/orgUnitTypes/hooks/useGetOrgUnitTypesDropdownOptions.ts index c6476a5702..4190b98f87 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/orgUnitTypes/hooks/useGetOrgUnitTypesDropdownOptions.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/orgUnitTypes/hooks/useGetOrgUnitTypesDropdownOptions.ts @@ -7,33 +7,41 @@ import { useSnackQuery } from '../../../../libs/apiHooks'; import { DropdownOptions } from '../../../../types/utils'; import { OrgunitTypesApi } from '../../types/orgunitTypes'; -const getOrgunitTypes = (): Promise => { - return getRequest('/api/v2/orgunittypes/'); +const getOrgunitTypes = (projectId?: number): Promise => { + return getRequest( + projectId + ? `/api/v2/orgunittypes/?project=${projectId}` + : '/api/v2/orgunittypes/', + ); }; -export const useGetOrgUnitTypesDropdownOptions = (): UseQueryResult< - DropdownOptions[], - Error -> => { - const queryKey: any[] = ['orgunittypes-dropdown']; - return useSnackQuery(queryKey, () => getOrgunitTypes(), undefined, { - keepPreviousData: true, - staleTime: 1000 * 60 * 15, // in MS - cacheTime: 1000 * 60 * 5, - select: data => { - if (!data) return []; - return data.orgUnitTypes - .sort((orgunitType1, orgunitType2) => { - const depth1 = orgunitType1.depth ?? 0; - const depth2 = orgunitType2.depth ?? 0; - return depth1 < depth2 ? -1 : 1; - }) - .map(orgunitType => { - return { - value: orgunitType.id.toString(), - label: orgunitType.name, - }; - }); +export const useGetOrgUnitTypesDropdownOptions = ( + projectId?: number, +): UseQueryResult[], Error> => { + const queryKey: any[] = ['orgunittypes-dropdown', projectId]; + return useSnackQuery( + queryKey, + () => getOrgunitTypes(projectId), + undefined, + { + keepPreviousData: true, + staleTime: 1000 * 60 * 15, // in MS + cacheTime: 1000 * 60 * 5, + select: data => { + if (!data) return []; + return data.orgUnitTypes + .sort((orgunitType1, orgunitType2) => { + const depth1 = orgunitType1.depth ?? 0; + const depth2 = orgunitType2.depth ?? 0; + return depth1 < depth2 ? -1 : 1; + }) + .map(orgunitType => { + return { + value: orgunitType.id.toString(), + label: orgunitType.name, + }; + }); + }, }, - }); + ); }; From 662fa0f39cae1b1053234e30300aa71fec4ec55b Mon Sep 17 00:00:00 2001 From: Christophe Gerard Date: Tue, 1 Oct 2024 10:43:48 +0200 Subject: [PATCH 21/22] code review --- .../OrgUnitChangeRequestConfigDialog.tsx | 104 +++++++++--------- .../Tables/EditableFieldsCell.tsx | 7 +- ...seSaveOrgUnitChangeRequestConfiguration.ts | 21 ++-- .../hooks/useValidationSchemaOUCRC.ts | 21 +--- iaso/api/group_sets/views.py | 5 +- 5 files changed, 71 insertions(+), 87 deletions(-) diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx index 548a0c8eb9..05555477c9 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Dialog/OrgUnitChangeRequestConfigDialog.tsx @@ -170,10 +170,10 @@ const OrgUnitChangeRequestConfigDialog: FunctionComponent = ({ > {isLoadingFullConfig && } - Project: {config.project.name} + {formatMessage(MESSAGES.project)}: {config.project.name} - Org Unit Type: {config.orgUnitType.name} + {formatMessage(MESSAGES.orgUnitType)}: {config.orgUnitType.name} = ({ options={groupSetOptions} /> )} - {values?.editableFields && - values.editableFields.includes('possibleTypeIds') && ( - - )} - {values?.editableFields && - values.editableFields.includes('possibleParentTypeIds') && ( - - )} - {values?.editableFields && - values.editableFields.includes('editableReferenceFormIds') && ( - - )} - {values?.editableFields && - values.editableFields.includes('otherGroupIds') && ( - - )} + {values?.editableFields?.includes('possibleTypeIds') && ( + + )} + {values?.editableFields?.includes('possibleParentTypeIds') && ( + + )} + {values?.editableFields?.includes('editableReferenceFormIds') && ( + + )} + {values?.editableFields?.includes('otherGroupIds') && ( + + )} ); }; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/EditableFieldsCell.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/EditableFieldsCell.tsx index 9a1acca869..4f086fb45c 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/EditableFieldsCell.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Tables/EditableFieldsCell.tsx @@ -1,7 +1,7 @@ /* eslint-disable camelcase */ import { Box, Chip } from '@mui/material'; import { textPlaceholder, useSafeIntl } from 'bluesquare-components'; -import React from 'react'; +import React, { useMemo } from 'react'; import { computeEditableFields } from '../hooks/api/useRetrieveOrgUnitChangeRequestConfig'; import MESSAGES from '../messages'; import { OrgUnitChangeRequestConfigurationFull } from '../types'; @@ -12,7 +12,10 @@ export const EditableFieldsCell = ({ row: { original: OrgUnitChangeRequestConfigurationFull }; }) => { const { formatMessage } = useSafeIntl(); - const editableFields = computeEditableFields(original); + const editableFields = useMemo( + () => computeEditableFields(original), + [original], + ); if (editableFields.length === 0) { return textPlaceholder; diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts index c66129d65f..8e786a8eb6 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/api/useSaveOrgUnitChangeRequestConfiguration.ts @@ -14,6 +14,11 @@ const cleanEditableFieldsForSaving = (editableFields?: string): string[] => { }); }; +// All the many to many fields are added if they have a value +const splitAndMapToNumbers = (str?: string) => { + return str?.trim() ? str.split(',').map(Number) : []; +}; + type ApiValues = { org_units_editable: boolean; project_id?: number; @@ -44,11 +49,6 @@ const mapValuesForSaving = ( values.editableFields, ); - // All the many to many fields are added if they have a value - const splitAndMapToNumbers = (str?: string) => { - return str?.trim() ? str.split(',').map(Number) : []; - }; - apiValues.possible_type_ids = splitAndMapToNumbers(values.possibleTypeIds); apiValues.possible_parent_type_ids = splitAndMapToNumbers( values.possibleParentTypeIds, @@ -61,15 +61,12 @@ const mapValuesForSaving = ( return apiValues; }; -const patchOrgUniType = async ( - configId: number, - body: ApiValues, -): Promise => { +const patchOUCRC = async (configId: number, body: ApiValues): Promise => { const url = `${apiUrlOUCRC}${configId}/`; return patchRequest(url, body); }; -const postOrgUnitType = async (body: ApiValues): Promise => { +const postOUCRC = async (body: ApiValues): Promise => { return postRequest({ url: `${apiUrlOUCRC}`, data: body, @@ -89,8 +86,8 @@ export const useSaveOrgUnitChangeRequestConfiguration = }) => { const formattedData = mapValuesForSaving(configId, data); return configId - ? patchOrgUniType(configId, formattedData) - : postOrgUnitType(formattedData); + ? patchOUCRC(configId, formattedData) + : postOUCRC(formattedData); }, ignoreErrorCodes, invalidateQueryKey: [ diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts index 0ff1801207..c8fc7b4309 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/hooks/useValidationSchemaOUCRC.ts @@ -42,11 +42,7 @@ export const useValidationSchemaOUCRC = () => { .boolean() .nullable() .required(formatMessage(MESSAGES.requiredField)), - editableFields: yup.string().nullable().when('orgUnitsEditable', { - is: true, - then: yup.string().nullable(), - otherwise: yup.string().nullable(), - }), + editableFields: yup.string().nullable(), possibleTypeIds: yup .string() .nullable() @@ -71,18 +67,7 @@ export const useValidationSchemaOUCRC = () => { then: yup.string().nullable().isMultiSelectValid(formatMessage), otherwise: yup.string().nullable(), }), - editableReferenceFormIds: yup - .string() - .nullable() - .when('orgUnitsEditable', { - is: true, - then: yup.string().nullable(), - otherwise: yup.string().nullable(), - }), - otherGroupIds: yup.string().nullable().when('orgUnitsEditable', { - is: true, - then: yup.string().nullable(), - otherwise: yup.string().nullable(), - }), + editableReferenceFormIds: yup.string().nullable(), + otherGroupIds: yup.string().nullable(), }); }; diff --git a/iaso/api/group_sets/views.py b/iaso/api/group_sets/views.py index 3ddafeae42..c60baa368e 100644 --- a/iaso/api/group_sets/views.py +++ b/iaso/api/group_sets/views.py @@ -106,7 +106,10 @@ def dropdown(self, request, *args): else: # this check if project need auth - project = Project.objects.get_for_user_and_app_id(user, app_id) + try: + project = Project.objects.get_for_user_and_app_id(user, app_id) + except Project.DoesNotExist: + raise serializers.ValidationError("No project found for the given app_id") versions = SourceVersion.objects.filter(data_source__projects=project) group_sets = GroupSet.objects.filter(source_version__in=versions).distinct() From be08c36d397e1802b97b30d17b3e5bab8a6ea228 Mon Sep 17 00:00:00 2001 From: Christophe Gerard Date: Tue, 1 Oct 2024 12:15:51 +0200 Subject: [PATCH 22/22] fix filter button --- .../Filter/OrgUnitChangeRequestConfigsFilter.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Filter/OrgUnitChangeRequestConfigsFilter.tsx b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Filter/OrgUnitChangeRequestConfigsFilter.tsx index 23c6664efe..917d4d85c0 100644 --- a/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Filter/OrgUnitChangeRequestConfigsFilter.tsx +++ b/hat/assets/js/apps/Iaso/domains/orgUnits/configuration/Filter/OrgUnitChangeRequestConfigsFilter.tsx @@ -51,8 +51,16 @@ export const OrgUnitChangeRequestConfigsFilter: FunctionComponent = ({ labelString={formatMessage(MESSAGES.orgUnitType)} /> - - + +