From c1f4ff7b962d243c80b1c910e3eed1990e1d2505 Mon Sep 17 00:00:00 2001 From: caichi <54824604+caichi-t@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:50:25 +0900 Subject: [PATCH] fix(web): update the item with a new group field (#1037) * fix: id bug * fix: styled warning * fix: check if value changed * fix: adding new group field * fix handling multiple value and refactor * add: useCallback * undo: styled-component warning --- .../molecules/Common/Form/GroupItem/index.tsx | 22 +-- .../molecules/Content/Details/index.tsx | 2 +- .../molecules/Content/Form/index.tsx | 144 +++++++++++------- .../Project/Content/ContentDetails/hooks.ts | 104 ++++++------- 4 files changed, 150 insertions(+), 122 deletions(-) diff --git a/web/src/components/molecules/Common/Form/GroupItem/index.tsx b/web/src/components/molecules/Common/Form/GroupItem/index.tsx index bf19561a43..ec0ebd1610 100644 --- a/web/src/components/molecules/Common/Form/GroupItem/index.tsx +++ b/web/src/components/molecules/Common/Form/GroupItem/index.tsx @@ -118,7 +118,7 @@ const GroupItem: React.FC = ({ const t = useT(); const fields = useMemo(() => group?.schema.fields, [group?.schema.fields]); - const itemGroupId = useMemo(() => value, [value]); + const itemGroupId = useMemo(() => value ?? "", [value]); return ( @@ -154,7 +154,7 @@ const GroupItem: React.FC = ({ message: t("Please input field!"), }, ]} - name={[field.id, itemGroupId ?? ""]} + name={[field.id, itemGroupId]} label={ }> @@ -179,7 +179,7 @@ const GroupItem: React.FC = ({ message: t("Please input field!"), }, ]} - name={[field.id, itemGroupId ?? ""]} + name={[field.id, itemGroupId]} label={ }> @@ -202,7 +202,7 @@ const GroupItem: React.FC = ({ message: t("Please input field!"), }, ]} - name={[field.id, itemGroupId ?? ""]} + name={[field.id, itemGroupId]} label={ }> @@ -231,7 +231,7 @@ const GroupItem: React.FC = ({ message: t("Please input field!"), }, ]} - name={[field.id, itemGroupId ?? ""]} + name={[field.id, itemGroupId]} label={ }> @@ -288,7 +288,7 @@ const GroupItem: React.FC = ({ } @@ -314,7 +314,7 @@ const GroupItem: React.FC = ({ } @@ -334,7 +334,7 @@ const GroupItem: React.FC = ({ @@ -345,7 +345,7 @@ const GroupItem: React.FC = ({ }> = ({ } @@ -411,7 +411,7 @@ const GroupItem: React.FC = ({ message: t("Please input field!"), }, ]} - name={[field.id, itemGroupId ?? ""]} + name={[field.id, itemGroupId]} label={ }> diff --git a/web/src/components/molecules/Content/Details/index.tsx b/web/src/components/molecules/Content/Details/index.tsx index aa6cb585f9..20d307ea9f 100644 --- a/web/src/components/molecules/Content/Details/index.tsx +++ b/web/src/components/molecules/Content/Details/index.tsx @@ -71,7 +71,7 @@ export type Props = { setUploadType: (type: UploadType) => void; onItemCreate: (data: { schemaId: string; - metaSchemaId: string; + metaSchemaId?: string; fields: ItemField[]; metaFields: ItemField[]; }) => Promise; diff --git a/web/src/components/molecules/Content/Form/index.tsx b/web/src/components/molecules/Content/Form/index.tsx index 1f0cabbb7c..306d023215 100644 --- a/web/src/components/molecules/Content/Form/index.tsx +++ b/web/src/components/molecules/Content/Form/index.tsx @@ -35,9 +35,14 @@ import ContentSidebarWrapper from "@reearth-cms/components/molecules/Content/For import LinkItemRequestModal from "@reearth-cms/components/molecules/Content/LinkItemRequestModal/LinkItemRequestModal"; import PublishItemModal from "@reearth-cms/components/molecules/Content/PublishItemModal"; import RequestCreationModal from "@reearth-cms/components/molecules/Content/RequestCreationModal"; -import { Item, FormItem, ItemField } from "@reearth-cms/components/molecules/Content/types"; +import { + Item, + FormItem, + ItemField, + ItemValue, +} from "@reearth-cms/components/molecules/Content/types"; import { Request, RequestState } from "@reearth-cms/components/molecules/Request/types"; -import { FieldType, Group, Model } from "@reearth-cms/components/molecules/Schema/types"; +import { FieldType, Group, Model, Field } from "@reearth-cms/components/molecules/Schema/types"; import { Member } from "@reearth-cms/components/molecules/Workspace/types"; import { AssetSortType, @@ -96,7 +101,7 @@ export interface Props { setUploadType: (type: UploadType) => void; onItemCreate: (data: { schemaId: string; - metaSchemaId: string; + metaSchemaId?: string; fields: ItemField[]; metaFields: ItemField[]; }) => Promise; @@ -201,10 +206,32 @@ const ContentForm: React.FC = ({ currentLocation.pathname !== nextLocation.pathname && changedKeys.current.size > 0, ); + const checkIfSingleGroupField = useCallback( + (key: string, value: any) => { + return ( + initialFormValues[key] && + typeof value === "object" && + !Array.isArray(value) && + value !== null + ); + }, + [initialFormValues], + ); + const handleValuesChange = useCallback( (changedValues: any) => { const [key, value] = Object.entries(changedValues)[0]; - if ( + if (checkIfSingleGroupField(key, value)) { + const [groupFieldKey, groupFieldValue] = Object.entries(initialFormValues[key])[0]; + const changedFieldValue = (value as any)[groupFieldKey]; + if (changedFieldValue && groupFieldValue !== null) { + if (JSON.stringify(changedFieldValue) === JSON.stringify(groupFieldValue)) { + changedKeys.current.delete(key); + } else { + changedKeys.current.add(key); + } + } + } else if ( (!value && !initialFormValues[key]) || JSON.stringify(value) === JSON.stringify(initialFormValues[key]) ) { @@ -215,7 +242,7 @@ const ContentForm: React.FC = ({ changedKeys.current.add(key); } }, - [initialFormValues], + [checkIfSingleGroupField, initialFormValues], ); useEffect(() => { @@ -287,75 +314,81 @@ const ContentForm: React.FC = ({ [formItemsData], ); + const inputValueGet = useCallback((value: ItemValue, multiple: boolean) => { + if (multiple) { + if (Array.isArray(value)) { + return value.map(v => (moment.isMoment(v) ? transformMomentToString(v) : v)); + } else { + return []; + } + } else { + return moment.isMoment(value) ? transformMomentToString(value) : value ?? ""; + } + }, []); + const handleSubmit = useCallback(async () => { try { - const modelFieldTypes = new Map( - (model?.schema.fields || []).map(field => [field.id, field.type]), - ); + const modelFields = new Map((model?.schema.fields || []).map(field => [field.id, field])); const groupIdsInCurrentModel = new Set(); model?.schema.fields?.forEach(field => { if (field.type === "Group") groupIdsInCurrentModel.add(field.typeProperty?.groupId); }); - const groupFieldTypes = new Map(); + const groupFields = new Map(); groups ?.filter(group => groupIdsInCurrentModel.has(group.id)) .forEach(group => { - group?.schema.fields?.forEach(field => groupFieldTypes.set(field.id, field.type)); + group?.schema.fields?.forEach(field => groupFields.set(field.id, field)); }); const values = await form.validateFields(); - const metaValues = await metaForm.validateFields(); - const fields: { - schemaFieldId: string; - itemGroupId?: string; - type: FieldType; - value: string; - }[] = []; - const metaFields: { schemaFieldId: string; type: FieldType; value: string }[] = []; - // TODO: improve performance + const fields: ItemField[] = []; for (const [key, value] of Object.entries(values)) { - const isGroup = - typeof value === "object" && !Array.isArray(value) && !moment.isMoment(value); - // group fields - if (value && isGroup) { - for (const [key1, value1] of Object.entries(value)) { - const type1 = groupFieldTypes.get(key) || ""; - fields.push({ - value: (moment.isMoment(value1) - ? transformMomentToString(value1) - : value1 ?? "") as string, - schemaFieldId: key, - itemGroupId: key1, - type: type1 as FieldType, - }); + const modelField = modelFields.get(key); + if (modelField) { + fields.push({ + value: inputValueGet(value as ItemValue, modelField.multiple), + schemaFieldId: key, + type: modelField.type, + }); + } else if (typeof value === "object" && value !== null) { + for (const [groupFieldKey, groupFieldValue] of Object.entries(value)) { + const groupField = groupFields.get(key); + if (groupField) { + fields.push({ + value: inputValueGet(groupFieldValue, groupField.multiple), + schemaFieldId: key, + itemGroupId: groupFieldKey, + type: groupField.type, + }); + } } - continue; } - // model fields - const type = modelFieldTypes.get(key) || ""; - fields.push({ - value: (moment.isMoment(value) ? transformMomentToString(value) : value ?? "") as string, - schemaFieldId: key, - type: type as FieldType, - }); } + + const metaValues = await metaForm.validateFields(); + const metaFields: ItemField[] = []; for (const [key, value] of Object.entries(metaValues)) { - metaFields.push({ - value: (moment.isMoment(value) ? transformMomentToString(value) : value ?? "") as string, - schemaFieldId: key, - type: model?.metadataSchema?.fields?.find(field => field.id === key)?.type as FieldType, - }); + const type = model?.metadataSchema?.fields?.find(field => field.id === key)?.type; + if (type) { + metaFields.push({ + value: moment.isMoment(value) ? transformMomentToString(value) : value ?? "", + schemaFieldId: key, + type, + }); + } } + changedKeys.current.clear(); - if (!itemId) { - await onItemCreate?.({ - schemaId: model?.schema.id as string, - metaSchemaId: model?.metadataSchema?.id as string, - metaFields, + + if (itemId) { + await onItemUpdate?.({ + itemId: itemId, fields, }); - } else { - await onItemUpdate?.({ - itemId: itemId as string, + } else if (model?.schema.id) { + await onItemCreate?.({ + schemaId: model?.schema.id, + metaSchemaId: model?.metadataSchema?.id, + metaFields, fields, }); } @@ -371,8 +404,9 @@ const ContentForm: React.FC = ({ form, metaForm, itemId, - onItemCreate, + inputValueGet, onItemUpdate, + onItemCreate, ]); const handleMetaUpdate = useCallback(async () => { diff --git a/web/src/components/organisms/Project/Content/ContentDetails/hooks.ts b/web/src/components/organisms/Project/Content/ContentDetails/hooks.ts index 86dae00301..ad159f50a6 100644 --- a/web/src/components/organisms/Project/Content/ContentDetails/hooks.ts +++ b/web/src/components/organisms/Project/Content/ContentDetails/hooks.ts @@ -244,7 +244,7 @@ export default () => { metaFields, }: { schemaId: string; - metaSchemaId: string; + metaSchemaId?: string; fields: ItemField[]; metaFields: ItemField[]; }) => { @@ -350,75 +350,69 @@ export default () => { [dateConvert], ); - const initialFormValues: { [key: string]: any } = useMemo(() => { - const initialValues: { [key: string]: any } = {}; - if (!currentItem) { - const updateInitialValues = (value: any, id: string, itemGroupId: string) => { - if (typeof initialValues[id] === "object" && !Array.isArray(initialValues[id])) { - initialValues[id][itemGroupId] = value; + const updateValueConvert = useCallback( + ({ type, value }: ItemField) => { + if (type === "Group") { + if (value) { + return value; } else { - initialValues[id] = { [itemGroupId]: value }; + return newID(); } - }; + } else if (type === "Date") { + return dateConvert(value); + } else { + return value; + } + }, + [dateConvert], + ); - const groupInitialValuesUpdate = (group: Group, itemGroupId: string) => { - group?.schema?.fields?.forEach(field => { - updateInitialValues(valueGet(field), field.id, itemGroupId); - }); + const initialFormValues: { [key: string]: any } = useMemo(() => { + const initialValues: { [key: string]: any } = {}; + + const updateInitialValues = (value: any, id: string, itemGroupId: string) => { + initialValues[id] = { + ...initialValues[id], + ...{ [itemGroupId]: value }, }; + }; - currentModel?.schema.fields.forEach(field => { - switch (field.type) { - case "Select": - initialValues[field.id] = field.typeProperty?.selectDefaultValue; - break; - case "Integer": - initialValues[field.id] = field.typeProperty?.integerDefaultValue; - break; - case "Asset": - initialValues[field.id] = field.typeProperty?.assetDefaultValue; - break; - case "Group": - if (field.multiple) { - initialValues[field.id] = []; - } else { - const id = newID(); - initialValues[field.id] = id; - const group = groups?.find(group => group.id === field.typeProperty?.groupId); - if (group) groupInitialValuesUpdate(group, id); - } - break; - case "Date": - initialValues[field.id] = dateConvert(field.typeProperty?.defaultValue); - break; - default: - initialValues[field.id] = field.typeProperty?.defaultValue; - break; - } + const groupInitialValuesUpdate = (group: Group, itemGroupId: string) => { + group?.schema?.fields?.forEach(field => { + updateInitialValues(valueGet(field), field.id, itemGroupId); }); - } else { + }; + + if (currentItem) { currentItem?.fields?.forEach(field => { if (field.itemGroupId) { - if ( - typeof initialValues[field.schemaFieldId] === "object" && - !Array.isArray(initialValues[field.schemaFieldId]) && - !moment.isMoment(initialValues[field.schemaFieldId]) - ) { - initialValues[field.schemaFieldId][field.itemGroupId] = - field.type === "Date" ? dateConvert(field.value) : field.value; + initialValues[field.schemaFieldId] = { + ...initialValues[field.schemaFieldId], + ...{ [field.itemGroupId]: updateValueConvert(field) }, + }; + } else { + initialValues[field.schemaFieldId] = updateValueConvert(field); + } + }); + } else { + currentModel?.schema.fields.forEach(field => { + if (field.type === "Group") { + if (field.multiple) { + initialValues[field.id] = []; } else { - initialValues[field.schemaFieldId] = { - [field.itemGroupId]: field.type === "Date" ? dateConvert(field.value) : field.value, - }; + const id = newID(); + initialValues[field.id] = id; + const group = groups?.find(group => group.id === field.typeProperty?.groupId); + if (group) groupInitialValuesUpdate(group, id); } } else { - initialValues[field.schemaFieldId] = - field.type === "Date" ? dateConvert(field.value) : field.value; + initialValues[field.id] = valueGet(field); } }); } + return initialValues; - }, [currentItem, currentModel?.schema.fields, dateConvert, groups, valueGet]); + }, [currentItem, currentModel?.schema.fields, groups, updateValueConvert, valueGet]); const initialMetaFormValues: { [key: string]: any } = useMemo(() => { const initialValues: { [key: string]: any } = {};