Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IA-3725: Display deprecated possible fields in entity type dialog #1843

Merged
merged 3 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions hat/assets/js/apps/Iaso/domains/app/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@
"iaso.label.deletePlanning": "Delete planning : {planningName}",
"iaso.label.deleteText": "This operation cannot be undone.",
"iaso.label.deleteWarning": "Are you sure you want to delete {name}?",
"iaso.label.deprecated": "deprecated",
"iaso.label.derived": "Deduced from another form",
"iaso.label.destination": "Destination",
"iaso.label.details": "Details",
Expand Down
1 change: 1 addition & 0 deletions hat/assets/js/apps/Iaso/domains/app/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@
"iaso.label.deletePlanning": "Effacer le planning: {planningName}",
"iaso.label.deleteText": "Cette opération est définitive.",
"iaso.label.deleteWarning": "Êtes-vous certain(e) de vouloir supprimer {name}?",
"iaso.label.deprecated": "déprécié",
"iaso.label.derived": "Instances dérivées d'un autre formulaire (Pas de collecte mobile possible)",
"iaso.label.destination": "Destination",
"iaso.label.details": "Détails",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box } from '@mui/material';
import { Box, Chip } from '@mui/material';
import {
IconButton,
IntlFormatMessage,
Expand All @@ -7,7 +7,13 @@ import {
} from 'bluesquare-components';
import { FormikProps, FormikProvider, useFormik } from 'formik';
import isEqual from 'lodash/isEqual';
import React, { FunctionComponent, ReactNode, useState } from 'react';
import React, {
FunctionComponent,
ReactNode,
useCallback,
useMemo,
useState,
} from 'react';
import * as yup from 'yup';

import ConfirmCancelDialogComponent from '../../../../components/dialogs/ConfirmCancelDialogComponent';
Expand Down Expand Up @@ -129,6 +135,47 @@ export const EntityTypesDialog: FunctionComponent<Props> = ({
formId: values?.reference_form,
enabled: isOpen,
});

const renderTags = useCallback(
(tagValue, getTagProps) =>
tagValue
.sort((a, b) =>
formatLabel(a).localeCompare(formatLabel(b), undefined, {
sensitivity: 'accent',
}),
)
.map((option, index) => {
Comment on lines +142 to +146
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not really related but we should have that sort in a utils. I always have to ctrl+F it 😅

const field = possibleFields.find(
f => f.name === option.value,
);
return (
<Chip
color={
field?.is_latest
? 'primary'
: 'secondary'
}
label={option.label}
{...getTagProps({ index })}
/>
);
}),
[possibleFields],
);

const possibleFieldsOptions = useMemo(
() =>
possibleFields.map(field => ({
value: field.name,
label: field.is_latest
? formatLabel(field)
: `${formatLabel(field)} (${formatMessage(
MESSAGES.deprecated,
)})`,
})),
[formatMessage, possibleFields],
);

return (
<FormikProvider value={formik}>
{/* @ts-ignore */}
Expand Down Expand Up @@ -212,10 +259,8 @@ export const EntityTypesDialog: FunctionComponent<Props> = ({
}
value={!isFetchingForm ? values.fields_list_view : []}
label={MESSAGES.fieldsListView}
options={possibleFields.map(field => ({
value: field.name,
label: formatLabel(field),
}))}
options={possibleFieldsOptions}
renderTags={renderTags}
helperText={
isNew && !values.reference_form
? formatMessage(MESSAGES.selectReferenceForm)
Expand All @@ -238,10 +283,8 @@ export const EntityTypesDialog: FunctionComponent<Props> = ({
: []
}
label={MESSAGES.fieldsDetailInfoView}
options={possibleFields.map(field => ({
value: field.name,
label: formatLabel(field),
}))}
options={possibleFieldsOptions}
renderTags={renderTags}
helperText={
isNew && !values.reference_form
? formatMessage(MESSAGES.selectReferenceForm)
Expand All @@ -262,10 +305,8 @@ export const EntityTypesDialog: FunctionComponent<Props> = ({
: []
}
label={MESSAGES.fieldsDuplicateSearch}
options={possibleFields.map(field => ({
value: field.name,
label: formatLabel(field),
}))}
renderTags={renderTags}
options={possibleFieldsOptions}
helperText={
isNew && !values.reference_form
? formatMessage(MESSAGES.selectReferenceForm)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { UseQueryResult } from 'react-query';

import { useSnackQuery } from '../../../../../libs/apiHooks';
import { getRequest } from '../../../../../libs/Api';
import { useSnackQuery } from '../../../../../libs/apiHooks';

import { Form, PossibleField } from '../../../../forms/types/forms';
import { usePossibleFields } from '../../../../forms/hooks/useGetPossibleFields';
import { Form, PossibleField } from '../../../../forms/types/forms';

export const useGetForm = (
formId: number | undefined,
Expand Down Expand Up @@ -67,10 +67,14 @@ export const useGetFormForEntityType = ({
const { data: currentForm, isFetching: isFetchingForm } = useGetForm(
formId,
enabled && Boolean(formId),
'possible_fields,name',
'possible_fields_with_latest_version,name,latest_form_version',
);
return {
...usePossibleFields(isFetchingForm, currentForm),
...usePossibleFields(
isFetchingForm,
currentForm,
'possible_fields_with_latest_version',
),
name: currentForm?.name,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ const MESSAGES = defineMessages({
id: 'iaso.label.beneficiaries',
defaultMessage: 'Beneficiaries',
},
deprecated: {
id: 'iaso.label.deprecated',
defaultMessage: 'deprecated',
},
});

export default MESSAGES;
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { cloneDeep } from 'lodash';
import { useMemo } from 'react';
import { UseQueryResult } from 'react-query';
import { cloneDeep } from 'lodash';
import { DropdownOptions } from '../../../types/utils';
import {
useGetForm,
useGetForms,
} from '../../entities/entityTypes/hooks/requests/forms';

import { useSnackQuery } from '../../../libs/apiHooks';
import { getRequest } from '../../../libs/Api';
import { useSnackQuery } from '../../../libs/apiHooks';

import { Form, PossibleField } from '../types/forms';

Expand All @@ -29,18 +29,19 @@ type AllResults = {
export const usePossibleFields = (
isFetchingForm: boolean,
form?: Form,
possible_fields_key = 'possible_fields',
): Result => {
return useMemo(() => {
const possibleFields =
form?.possible_fields?.map(field => ({
form?.[possible_fields_key]?.map(field => ({
...field,
fieldKey: field.name.replace('.', ''),
})) || [];
return {
possibleFields,
isFetchingForm,
};
}, [form?.possible_fields, isFetchingForm]);
}, [form, isFetchingForm, possible_fields_key]);
};

export const useGetPossibleFields = (
Expand Down
1 change: 1 addition & 0 deletions hat/assets/js/apps/Iaso/domains/forms/types/forms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export type PossibleField = {
name: string;
type: FieldType;
fieldKey: string;
is_latest?: boolean;
};
export type ChildrenDescriptor = {
label: string;
Expand Down
29 changes: 22 additions & 7 deletions iaso/api/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@
from datetime import timedelta
from xml.sax.saxutils import escape

from django.db.models import Max, Q, Count
from django.http import StreamingHttpResponse, HttpResponse
from django.db.models import BooleanField, Case, Count, Max, Q, When
from django.http import HttpResponse, StreamingHttpResponse
from django.utils.dateparse import parse_date
from rest_framework import serializers, permissions, status
from rest_framework import permissions, serializers, status
from rest_framework.decorators import action
from rest_framework.generics import get_object_or_404
from rest_framework.request import Request
from django.db.models import Count, BooleanField, Case, When

from hat.api.export_utils import Echo, generate_xlsx, iter_items
from hat.audit.models import log_modification, FORM_API
from hat.audit.models import FORM_API, log_modification
from hat.menupermissions import models as permission
from iaso.models import Form, Project, OrgUnitType, OrgUnit, FormPredefinedFilter
from iaso.models import Form, FormPredefinedFilter, OrgUnit, OrgUnitType, Project
from iaso.utils import timestamp_to_datetime
from .common import ModelViewSet, TimestampField, DynamicFieldsModelSerializer, CONTENT_TYPE_XLSX, CONTENT_TYPE_CSV

from .common import CONTENT_TYPE_CSV, CONTENT_TYPE_XLSX, DynamicFieldsModelSerializer, ModelViewSet, TimestampField
from .enketo import public_url_for_enketo
from .projects import ProjectSerializer

Expand Down Expand Up @@ -107,6 +108,7 @@ class Meta:
"legend_threshold",
"change_request_mode",
"has_mappings",
"possible_fields_with_latest_version",
]
read_only_fields = [
"id",
Expand Down Expand Up @@ -142,6 +144,7 @@ class Meta:
has_attachments = serializers.SerializerMethodField()
reference_form_of_org_unit_types = serializers.SerializerMethodField()
has_mappings = serializers.BooleanField(read_only=True)
possible_fields_with_latest_version = serializers.SerializerMethodField()

@staticmethod
def get_latest_form_version(obj: Form):
Expand All @@ -159,6 +162,18 @@ def get_reference_form_of_org_unit_types(obj: Form):
def get_has_attachments(obj: Form):
return len(obj.attachments.all()) > 0

@staticmethod
def get_possible_fields_with_latest_version(obj: Form):
latest_version = obj.latest_version
if not latest_version:
return obj.possible_fields

# Get the field names from the latest version
latest_version_fields = set(question["name"] for question in latest_version.questions_by_name().values())

# Add a flag to each possible field indicating if it's part of the latest version
return [{**field, "is_latest": field["name"] in latest_version_fields} for field in obj.possible_fields]

def validate(self, data: typing.Mapping):
# validate projects (access check)
if "projects" in data:
Expand Down
Loading