import { FetchResult } from "@apollo/client"; import { AttributeInput, AttributeInputData } from "@saleor/components/Attributes"; import { AttributeEntityTypeEnum, AttributeInputTypeEnum, AttributeValueDeleteMutation, AttributeValueDeleteMutationVariables, AttributeValueInput, FileUploadMutation, FileUploadMutationVariables, PageSelectedAttributeFragment, ProductFragment, ProductVariantDetailsQuery } from "@saleor/graphql"; import { FormsetAtomicData, FormsetChange, FormsetData } from "@saleor/hooks/useFormset"; import { FetchMoreProps, ReorderEvent } from "@saleor/types"; import { move, toggle } from "@saleor/utils/lists"; import { getFileValuesToUploadFromAttributes, isFileValueUnused } from "./data"; export function createAttributeChangeHandler( changeAttributeData: FormsetChange, triggerChange: () => void ): FormsetChange { return (attributeId: string, value: string) => { triggerChange(); changeAttributeData(attributeId, value === "" ? [] : [value]); }; } export function createAttributeMultiChangeHandler( changeAttributeData: FormsetChange, attributes: FormsetData, triggerChange: () => void ): FormsetChange { return (attributeId: string, value: string) => { const attribute = attributes.find( attribute => attribute.id === attributeId ); const newAttributeValues = toggle( value, attribute.value, (a, b) => a === b ); triggerChange(); changeAttributeData(attributeId, newAttributeValues); }; } export function createAttributeReferenceChangeHandler( changeAttributeData: FormsetChange, triggerChange: () => void ): FormsetChange { return (attributeId: string, values: string[]) => { changeAttributeData(attributeId, values); triggerChange(); }; } export function createFetchReferencesHandler( attributes: FormsetData, assignReferencesAttributeId: string, fetchReferencePages?: (data: string) => void, fetchReferenceProducts?: (data: string) => void ) { return (value: string) => { const attribute = attributes?.find( attribute => attribute.id === assignReferencesAttributeId ); if (!attribute) { return; } if ( attribute.data.entityType === AttributeEntityTypeEnum.PAGE && fetchReferencePages ) { fetchReferencePages(value); } else if ( attribute.data.entityType === AttributeEntityTypeEnum.PRODUCT && fetchReferenceProducts ) { fetchReferenceProducts(value); } }; } export function createFetchMoreReferencesHandler( attributes: FormsetData, assignReferencesAttributeId: string, fetchMoreReferencePages?: FetchMoreProps, fetchMoreReferenceProducts?: FetchMoreProps ) { const attribute = attributes?.find( attribute => attribute.id === assignReferencesAttributeId ); if (!attribute) { return; } if (attribute.data.entityType === AttributeEntityTypeEnum.PAGE) { return fetchMoreReferencePages; } else if (attribute.data.entityType === AttributeEntityTypeEnum.PRODUCT) { return fetchMoreReferenceProducts; } } export function createAttributeFileChangeHandler( changeAttributeData: FormsetChange, attributesWithNewFileValue: FormsetData>, addAttributeNewFileValue: (data: FormsetAtomicData) => void, changeAttributeNewFileValue: FormsetChange, triggerChange: () => void ): FormsetChange { return (attributeId: string, value: File) => { triggerChange(); const newFileValueAssigned = attributesWithNewFileValue.find( attribute => attribute.id === attributeId ); if (newFileValueAssigned) { changeAttributeNewFileValue(attributeId, value); } else { addAttributeNewFileValue({ data: null, id: attributeId, label: null, value }); } changeAttributeData(attributeId, value ? [value.name] : []); }; } export function createAttributeValueReorderHandler( changeAttributeData: FormsetChange, attributes: FormsetData, triggerChange: () => void ): FormsetChange { return (attributeId: string, reorder: ReorderEvent) => { triggerChange(); const attribute = attributes.find( attribute => attribute.id === attributeId ); const reorderedValues = move( attribute.value[reorder.oldIndex], attribute.value, (a, b) => a === b, reorder.newIndex ); changeAttributeData(attributeId, reorderedValues); }; } interface AttributesArgs { attributes: AttributeInput[]; updatedFileAttributes: AttributeValueInput[]; } function getFileInput( attribute: AttributeInput, updatedFileAttributes: AttributeValueInput[] ) { const updatedFileAttribute = updatedFileAttributes.find( attributeWithNewFile => attribute.id === attributeWithNewFile.id ); if (updatedFileAttribute) { return { file: updatedFileAttribute.file, id: updatedFileAttribute.id, contentType: updatedFileAttribute.contentType }; } return { file: attribute.data.selectedValues?.[0]?.file?.url, contentType: attribute.data.selectedValues?.[0]?.file.contentType, id: attribute.id }; } function getBooleanInput(attribute: AttributeInput) { return { id: attribute.id, boolean: JSON.parse(attribute.value[0] ?? "false") }; } export const prepareAttributesInput = ({ attributes, updatedFileAttributes }: AttributesArgs): AttributeValueInput[] => attributes.reduce((attrInput, attr) => { const inputType = attr.data.inputType; if (inputType === AttributeInputTypeEnum.FILE) { const fileInput = getFileInput(attr, updatedFileAttributes); if (fileInput.file) { attrInput.push(fileInput); } return attrInput; } if (inputType === AttributeInputTypeEnum.BOOLEAN) { const booleanInput = getBooleanInput(attr); attrInput.push(booleanInput); return attrInput; } if (inputType === AttributeInputTypeEnum.RICH_TEXT) { attrInput.push({ id: attr.id, richText: attr.value[0] }); return attrInput; } // for cases other than rich text, boolean and file // we can skip attribute if (!attr.value[0]) { return attrInput; } if (inputType === AttributeInputTypeEnum.REFERENCE) { attrInput.push({ id: attr.id, references: attr.value }); return attrInput; } if (inputType === AttributeInputTypeEnum.DATE) { attrInput.push({ id: attr.id, date: attr.value[0] }); return attrInput; } if (inputType === AttributeInputTypeEnum.DATE_TIME) { attrInput.push({ id: attr.id, dateTime: attr.value[0] }); return attrInput; } attrInput.push({ id: attr.id, values: attr.value }); return attrInput; }, []); export const handleUploadMultipleFiles = async ( attributesWithNewFileValue: FormsetData, uploadFile: ( variables: FileUploadMutationVariables ) => Promise> ) => Promise.all( getFileValuesToUploadFromAttributes(attributesWithNewFileValue).map( fileAttribute => uploadFile({ file: fileAttribute.value }) ) ); export const handleDeleteMultipleAttributeValues = async ( attributesWithNewFileValue: FormsetData, attributes: Array< | PageSelectedAttributeFragment | ProductFragment["attributes"][0] | ProductVariantDetailsQuery["productVariant"]["nonSelectionAttributes"][0] >, deleteAttributeValue: ( variables: AttributeValueDeleteMutationVariables ) => Promise> ) => Promise.all( attributes.map(existingAttribute => { const fileValueUnused = isFileValueUnused( attributesWithNewFileValue, existingAttribute ); if (fileValueUnused) { return deleteAttributeValue({ id: existingAttribute.values[0].id, firstValues: 20 }); } }) );