diff --git a/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap b/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap index 812400b1..bc9b7ca8 100644 --- a/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap +++ b/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap @@ -10345,7 +10345,7 @@ export default function MyPostForm(props: MyPostFormProps): React.ReactElement; " `; -exports[`amplify form renderer tests GraphQL form tests should generate an update form with composite primary key 1`] = ` +exports[`amplify form renderer tests GraphQL form tests should generate an update form with composite primary key - amplify js v6 1`] = ` "/* eslint-disable */ import * as React from \\"react\\"; import { @@ -10369,12 +10369,13 @@ import { listTags, movieTagsByMovieMovieKeyAndMovietitleAndMoviegenre, } from \\"../graphql/queries\\"; -import { API } from \\"aws-amplify\\"; +import { generateClient } from \\"aws-amplify/api\\"; import { createMovieTags, deleteMovieTags, updateMovie, } from \\"../graphql/mutations\\"; +const client = generateClient(); function ArrayField({ items = [], onChange, @@ -10578,7 +10579,7 @@ export default function MovieUpdateForm(props) { const queryData = async () => { const record = idProp ? ( - await API.graphql({ + await client.graphql({ query: getMovie.replaceAll(\\"__typename\\", \\"\\"), variables: { ...idProp }, }) @@ -10586,7 +10587,7 @@ export default function MovieUpdateForm(props) { : movieModelProp; const linkedTags = record ? ( - await API.graphql({ + await client.graphql({ query: movieTagsByMovieMovieKeyAndMovietitleAndMoviegenre.replaceAll( \\"__typename\\", @@ -10662,7 +10663,7 @@ export default function MovieUpdateForm(props) { variables[\\"nextToken\\"] = newNext; } const result = ( - await API.graphql({ + await client.graphql({ query: listTags.replaceAll(\\"__typename\\", \\"\\"), variables, }) @@ -10770,7 +10771,7 @@ export default function MovieUpdateForm(props) { tagsToUnLinkMap.forEach(async (count, id) => { const recordKeys = JSON.parse(id); const movieTagsRecords = ( - await API.graphql({ + await client.graphql({ query: listMovieTags.replaceAll(\\"__typename\\", \\"\\"), variables: { filter: { @@ -10786,7 +10787,7 @@ export default function MovieUpdateForm(props) { )?.data?.listMovieTags?.items; for (let i = 0; i < count; i++) { promises.push( - API.graphql({ + client.graphql({ query: deleteMovieTags.replaceAll(\\"__typename\\", \\"\\"), variables: { input: { @@ -10805,7 +10806,7 @@ export default function MovieUpdateForm(props) { ); for (let i = count; i > 0; i--) { promises.push( - API.graphql({ + client.graphql({ query: createMovieTags.replaceAll(\\"__typename\\", \\"\\"), variables: { input: { @@ -10826,7 +10827,7 @@ export default function MovieUpdateForm(props) { rating: modelFields.rating ?? null, }; promises.push( - API.graphql({ + client.graphql({ query: updateMovie.replaceAll(\\"__typename\\", \\"\\"), variables: { input: { @@ -11076,7 +11077,7 @@ export default function MovieUpdateForm(props) { " `; -exports[`amplify form renderer tests GraphQL form tests should generate an update form with composite primary key 2`] = ` +exports[`amplify form renderer tests GraphQL form tests should generate an update form with composite primary key - amplify js v6 2`] = ` "import * as React from \\"react\\"; import { AutocompleteProps, GridProps, TextFieldProps } from \\"@aws-amplify/ui-react\\"; import { EscapeHatchProps } from \\"@aws-amplify/ui-react/internal\\"; @@ -11128,7 +11129,7 @@ export default function MovieUpdateForm(props: MovieUpdateFormProps): React.Reac " `; -exports[`amplify form renderer tests GraphQL form tests should generate an update form with hasMany relationship 1`] = ` +exports[`amplify form renderer tests GraphQL form tests should generate an update form with composite primary key 1`] = ` "/* eslint-disable */ import * as React from \\"react\\"; import { @@ -11146,9 +11147,18 @@ import { } from \\"@aws-amplify/ui-react\\"; import { getOverrideProps } from \\"@aws-amplify/ui-react/internal\\"; import { fetchByPath, validateField } from \\"./utils\\"; +import { + getMovie, + listMovieTags, + listTags, + movieTagsByMovieMovieKeyAndMovietitleAndMoviegenre, +} from \\"../graphql/queries\\"; import { API } from \\"aws-amplify\\"; -import { getComment, getPost, listPosts } from \\"../graphql/queries\\"; -import { updateComment } from \\"../graphql/mutations\\"; +import { + createMovieTags, + deleteMovieTags, + updateMovie, +} from \\"../graphql/mutations\\"; function ArrayField({ items = [], onChange, @@ -11304,10 +11314,10 @@ function ArrayField({ ); } -export default function CommentUpdateForm(props) { +export default function MovieUpdateForm(props) { const { id: idProp, - comment: commentModelProp, + movie: movieModelProp, onSuccess, onError, onSubmit, @@ -11317,90 +11327,92 @@ export default function CommentUpdateForm(props) { ...rest } = props; const initialValues = { - content: \\"\\", - postID: undefined, - Post: undefined, - post: \\"\\", + movieKey: \\"\\", + title: \\"\\", + genre: \\"\\", + rating: \\"\\", + tags: [], }; - const [content, setContent] = React.useState(initialValues.content); - const [postID, setPostID] = React.useState(initialValues.postID); - const [postIDLoading, setPostIDLoading] = React.useState(false); - const [postIDRecords, setPostIDRecords] = React.useState([]); - const [selectedPostIDRecords, setSelectedPostIDRecords] = React.useState([]); - const [Post, setPost] = React.useState(initialValues.Post); - const [PostLoading, setPostLoading] = React.useState(false); - const [PostRecords, setPostRecords] = React.useState([]); - const [post1, setPost1] = React.useState(initialValues.post); + const [movieKey, setMovieKey] = React.useState(initialValues.movieKey); + const [title, setTitle] = React.useState(initialValues.title); + const [genre, setGenre] = React.useState(initialValues.genre); + const [rating, setRating] = React.useState(initialValues.rating); + const [tags, setTags] = React.useState(initialValues.tags); + const [tagsLoading, setTagsLoading] = React.useState(false); + const [tagsRecords, setTagsRecords] = React.useState([]); const autocompleteLength = 10; const [errors, setErrors] = React.useState({}); const resetStateValues = () => { - const cleanValues = commentRecord - ? { ...initialValues, ...commentRecord, postID, Post } + const cleanValues = movieRecord + ? { ...initialValues, ...movieRecord, tags: linkedTags } : initialValues; - setContent(cleanValues.content); - setPostID(cleanValues.postID); - setCurrentPostIDValue(undefined); - setCurrentPostIDDisplayValue(\\"\\"); - setPost(cleanValues.Post); - setCurrentPostValue(undefined); - setCurrentPostDisplayValue(\\"\\"); - setPost1(cleanValues.post); + setMovieKey(cleanValues.movieKey); + setTitle(cleanValues.title); + setGenre(cleanValues.genre); + setRating(cleanValues.rating); + setTags(cleanValues.tags ?? []); + setCurrentTagsValue(undefined); + setCurrentTagsDisplayValue(\\"\\"); setErrors({}); }; - const [commentRecord, setCommentRecord] = React.useState(commentModelProp); + const [movieRecord, setMovieRecord] = React.useState(movieModelProp); + const [linkedTags, setLinkedTags] = React.useState([]); + const canUnlinkTags = false; React.useEffect(() => { const queryData = async () => { const record = idProp ? ( await API.graphql({ - query: getComment.replaceAll(\\"__typename\\", \\"\\"), - variables: { id: idProp }, + query: getMovie.replaceAll(\\"__typename\\", \\"\\"), + variables: { ...idProp }, }) - )?.data?.getComment - : commentModelProp; - const postIDRecord = record ? record.postID : undefined; - const postRecord = postIDRecord + )?.data?.getMovie + : movieModelProp; + const linkedTags = record ? ( await API.graphql({ - query: getPost.replaceAll(\\"__typename\\", \\"\\"), - variables: { id: postIDRecord }, + query: + movieTagsByMovieMovieKeyAndMovietitleAndMoviegenre.replaceAll( + \\"__typename\\", + \\"\\" + ), + variables: { + movieMovieKey: record.movieKey, + movietitle: record.title, + moviegenre: record.genre, + }, }) - )?.data?.getPost - : undefined; - setPostID(postIDRecord); - setSelectedPostIDRecords([postRecord]); - const PostRecord = record ? await record.Post : undefined; - setPost(PostRecord); - setCommentRecord(record); + ).data.movieTagsByMovieMovieKeyAndMovietitleAndMoviegenre.items.map( + (t) => t.tag + ) + : []; + setLinkedTags(linkedTags); + setMovieRecord(record); }; queryData(); - }, [idProp, commentModelProp]); - React.useEffect(resetStateValues, [commentRecord, postID, Post]); - const [currentPostIDDisplayValue, setCurrentPostIDDisplayValue] = - React.useState(\\"\\"); - const [currentPostIDValue, setCurrentPostIDValue] = React.useState(undefined); - const postIDRef = React.createRef(); - const [currentPostDisplayValue, setCurrentPostDisplayValue] = + }, [idProp, movieModelProp]); + React.useEffect(resetStateValues, [movieRecord, linkedTags]); + const [currentTagsDisplayValue, setCurrentTagsDisplayValue] = React.useState(\\"\\"); - const [currentPostValue, setCurrentPostValue] = React.useState(undefined); - const PostRef = React.createRef(); + const [currentTagsValue, setCurrentTagsValue] = React.useState(undefined); + const tagsRef = React.createRef(); const getIDValue = { - Post: (r) => JSON.stringify({ id: r?.id }), + tags: (r) => JSON.stringify({ id: r?.id }), }; - const PostIdSet = new Set( - Array.isArray(Post) - ? Post.map((r) => getIDValue.Post?.(r)) - : getIDValue.Post?.(Post) + const tagsIdSet = new Set( + Array.isArray(tags) + ? tags.map((r) => getIDValue.tags?.(r)) + : getIDValue.tags?.(tags) ); const getDisplayValue = { - postID: (r) => \`\${r?.title ? r?.title + \\" - \\" : \\"\\"}\${r?.id}\`, - Post: (r) => \`\${r?.title ? r?.title + \\" - \\" : \\"\\"}\${r?.id}\`, + tags: (r) => \`\${r?.label ? r?.label + \\" - \\" : \\"\\"}\${r?.id}\`, }; const validations = { - content: [], - postID: [{ type: \\"Required\\" }], - Post: [], - post: [], + movieKey: [{ type: \\"Required\\" }], + title: [{ type: \\"Required\\" }], + genre: [{ type: \\"Required\\" }], + rating: [], + tags: [], }; const runValidationTasks = async ( fieldName, @@ -11419,42 +11431,15 @@ export default function CommentUpdateForm(props) { setErrors((errors) => ({ ...errors, [fieldName]: validationResponse })); return validationResponse; }; - const fetchPostIDRecords = async (value) => { - setPostIDLoading(true); - const newOptions = []; - let newNext = \\"\\"; - while (newOptions.length < autocompleteLength && newNext != null) { - const variables = { - limit: autocompleteLength * 5, - filter: { - or: [{ title: { contains: value } }, { id: { contains: value } }], - }, - }; - if (newNext) { - variables[\\"nextToken\\"] = newNext; - } - const result = ( - await API.graphql({ - query: listPosts.replaceAll(\\"__typename\\", \\"\\"), - variables, - }) - )?.data?.listPosts?.items; - var loaded = result.filter((item) => postID !== item.id); - newOptions.push(...loaded); - newNext = result.nextToken; - } - setPostIDRecords(newOptions.slice(0, autocompleteLength)); - setPostIDLoading(false); - }; - const fetchPostRecords = async (value) => { - setPostLoading(true); + const fetchTagsRecords = async (value) => { + setTagsLoading(true); const newOptions = []; let newNext = \\"\\"; while (newOptions.length < autocompleteLength && newNext != null) { const variables = { limit: autocompleteLength * 5, filter: { - or: [{ title: { contains: value } }, { id: { contains: value } }], + or: [{ label: { contains: value } }, { id: { contains: value } }], }, }; if (newNext) { @@ -11462,22 +11447,21 @@ export default function CommentUpdateForm(props) { } const result = ( await API.graphql({ - query: listPosts.replaceAll(\\"__typename\\", \\"\\"), + query: listTags.replaceAll(\\"__typename\\", \\"\\"), variables, }) - )?.data?.listPosts?.items; + )?.data?.listTags?.items; var loaded = result.filter( - (item) => !PostIdSet.has(getIDValue.Post?.(item)) + (item) => !tagsIdSet.has(getIDValue.tags?.(item)) ); newOptions.push(...loaded); newNext = result.nextToken; } - setPostRecords(newOptions.slice(0, autocompleteLength)); - setPostLoading(false); + setTagsRecords(newOptions.slice(0, autocompleteLength)); + setTagsLoading(false); }; React.useEffect(() => { - fetchPostIDRecords(\\"\\"); - fetchPostRecords(\\"\\"); + fetchTagsRecords(\\"\\"); }, []); return ( { event.preventDefault(); let modelFields = { - content: content ?? null, - postID, - Post: Post ?? null, - post: post ?? null, + movieKey, + title, + genre, + rating: rating ?? null, + tags: tags ?? null, }; const validationResponses = await Promise.all( Object.keys(validations).reduce((promises, fieldName) => { @@ -11529,43 +11514,2308 @@ export default function CommentUpdateForm(props) { modelFields[key] = null; } }); - const modelFieldsToSave = { - content: modelFields.content ?? null, - postID: modelFields.postID, - postCommentsId: modelFields?.Post?.id ?? null, - }; - await API.graphql({ - query: updateComment.replaceAll(\\"__typename\\", \\"\\"), - variables: { - input: { - id: commentRecord.id, - ...modelFieldsToSave, - }, - }, + const promises = []; + const tagsToLinkMap = new Map(); + const tagsToUnLinkMap = new Map(); + const tagsMap = new Map(); + const linkedTagsMap = new Map(); + tags.forEach((r) => { + const count = tagsMap.get(getIDValue.tags?.(r)); + const newCount = count ? count + 1 : 1; + tagsMap.set(getIDValue.tags?.(r), newCount); }); - if (onSuccess) { - onSuccess(modelFields); - } - } catch (err) { - if (onError) { - const messages = err.errors.map((e) => e.message).join(\\"\\\\n\\"); - onError(modelFields, messages); - } - } - }} - {...getOverrideProps(overrides, \\"CommentUpdateForm\\")} - {...rest} - > - { - let { value } = e.target; - if (onChange) { - const modelFields = { - content: value, + linkedTags.forEach((r) => { + const count = linkedTagsMap.get(getIDValue.tags?.(r)); + const newCount = count ? count + 1 : 1; + linkedTagsMap.set(getIDValue.tags?.(r), newCount); + }); + linkedTagsMap.forEach((count, id) => { + const newCount = tagsMap.get(id); + if (newCount) { + const diffCount = count - newCount; + if (diffCount > 0) { + tagsToUnLinkMap.set(id, diffCount); + } + } else { + tagsToUnLinkMap.set(id, count); + } + }); + tagsMap.forEach((count, id) => { + const originalCount = linkedTagsMap.get(id); + if (originalCount) { + const diffCount = count - originalCount; + if (diffCount > 0) { + tagsToLinkMap.set(id, diffCount); + } + } else { + tagsToLinkMap.set(id, count); + } + }); + tagsToUnLinkMap.forEach(async (count, id) => { + const recordKeys = JSON.parse(id); + const movieTagsRecords = ( + await API.graphql({ + query: listMovieTags.replaceAll(\\"__typename\\", \\"\\"), + variables: { + filter: { + and: [ + { tagId: { eq: recordKeys.id } }, + { movieMovieKey: { eq: movieRecord.movieKey } }, + { movietitle: { eq: movieRecord.title } }, + { moviegenre: { eq: movieRecord.genre } }, + ], + }, + }, + }) + )?.data?.listMovieTags?.items; + for (let i = 0; i < count; i++) { + promises.push( + API.graphql({ + query: deleteMovieTags.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + id: movieTagsRecords[i].id, + }, + }, + }) + ); + } + }); + tagsToLinkMap.forEach((count, id) => { + const tagToLink = tagRecords.find((r) => + Object.entries(JSON.parse(id)).every( + ([key, value]) => r[key] === value + ) + ); + for (let i = count; i > 0; i--) { + promises.push( + API.graphql({ + query: createMovieTags.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + movieMovieKey: movieRecord.movieKey, + movietitle: movieRecord.title, + moviegenre: movieRecord.genre, + tagId: tagToLink.id, + }, + }, + }) + ); + } + }); + const modelFieldsToSave = { + movieKey: modelFields.movieKey, + title: modelFields.title, + genre: modelFields.genre, + rating: modelFields.rating ?? null, + }; + promises.push( + API.graphql({ + query: updateMovie.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + movieKey: movieRecord.movieKey, + title: movieRecord.title, + genre: movieRecord.genre, + ...modelFieldsToSave, + }, + }, + }) + ); + await Promise.all(promises); + if (onSuccess) { + onSuccess(modelFields); + } + } catch (err) { + if (onError) { + const messages = err.errors.map((e) => e.message).join(\\"\\\\n\\"); + onError(modelFields, messages); + } + } + }} + {...getOverrideProps(overrides, \\"MovieUpdateForm\\")} + {...rest} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + movieKey: value, + title, + genre, + rating, + tags, + }; + const result = onChange(modelFields); + value = result?.movieKey ?? value; + } + if (errors.movieKey?.hasError) { + runValidationTasks(\\"movieKey\\", value); + } + setMovieKey(value); + }} + onBlur={() => runValidationTasks(\\"movieKey\\", movieKey)} + errorMessage={errors.movieKey?.errorMessage} + hasError={errors.movieKey?.hasError} + {...getOverrideProps(overrides, \\"movieKey\\")} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + movieKey, + title: value, + genre, + rating, + tags, + }; + const result = onChange(modelFields); + value = result?.title ?? value; + } + if (errors.title?.hasError) { + runValidationTasks(\\"title\\", value); + } + setTitle(value); + }} + onBlur={() => runValidationTasks(\\"title\\", title)} + errorMessage={errors.title?.errorMessage} + hasError={errors.title?.hasError} + {...getOverrideProps(overrides, \\"title\\")} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + movieKey, + title, + genre: value, + rating, + tags, + }; + const result = onChange(modelFields); + value = result?.genre ?? value; + } + if (errors.genre?.hasError) { + runValidationTasks(\\"genre\\", value); + } + setGenre(value); + }} + onBlur={() => runValidationTasks(\\"genre\\", genre)} + errorMessage={errors.genre?.errorMessage} + hasError={errors.genre?.hasError} + {...getOverrideProps(overrides, \\"genre\\")} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + movieKey, + title, + genre, + rating: value, + tags, + }; + const result = onChange(modelFields); + value = result?.rating ?? value; + } + if (errors.rating?.hasError) { + runValidationTasks(\\"rating\\", value); + } + setRating(value); + }} + onBlur={() => runValidationTasks(\\"rating\\", rating)} + errorMessage={errors.rating?.errorMessage} + hasError={errors.rating?.hasError} + {...getOverrideProps(overrides, \\"rating\\")} + > + { + let values = items; + if (onChange) { + const modelFields = { + movieKey, + title, + genre, + rating, + tags: values, + }; + const result = onChange(modelFields); + values = result?.tags ?? values; + } + setTags(values); + setCurrentTagsValue(undefined); + setCurrentTagsDisplayValue(\\"\\"); + }} + currentFieldValue={currentTagsValue} + label={\\"Tags\\"} + items={tags} + hasError={errors?.tags?.hasError} + runValidationTasks={async () => + await runValidationTasks(\\"tags\\", currentTagsValue) + } + errorMessage={errors?.tags?.errorMessage} + getBadgeText={getDisplayValue.tags} + setFieldValue={(model) => { + setCurrentTagsDisplayValue(model ? getDisplayValue.tags(model) : \\"\\"); + setCurrentTagsValue(model); + }} + inputFieldRef={tagsRef} + defaultFieldValue={\\"\\"} + > + ({ + id: getIDValue.tags?.(r), + label: getDisplayValue.tags?.(r), + }))} + isLoading={tagsLoading} + onSelect={({ id, label }) => { + setCurrentTagsValue( + tagsRecords.find((r) => + Object.entries(JSON.parse(id)).every( + ([key, value]) => r[key] === value + ) + ) + ); + setCurrentTagsDisplayValue(label); + runValidationTasks(\\"tags\\", label); + }} + onClear={() => { + setCurrentTagsDisplayValue(\\"\\"); + }} + onChange={(e) => { + let { value } = e.target; + fetchTagsRecords(value); + if (errors.tags?.hasError) { + runValidationTasks(\\"tags\\", value); + } + setCurrentTagsDisplayValue(value); + setCurrentTagsValue(undefined); + }} + onBlur={() => runValidationTasks(\\"tags\\", currentTagsDisplayValue)} + errorMessage={errors.tags?.errorMessage} + hasError={errors.tags?.hasError} + ref={tagsRef} + labelHidden={true} + {...getOverrideProps(overrides, \\"tags\\")} + > + + + + + + + + + ); +} +" +`; + +exports[`amplify form renderer tests GraphQL form tests should generate an update form with composite primary key 2`] = ` +"import * as React from \\"react\\"; +import { AutocompleteProps, GridProps, TextFieldProps } from \\"@aws-amplify/ui-react\\"; +import { EscapeHatchProps } from \\"@aws-amplify/ui-react/internal\\"; +import { Movie, Tag } from \\"../API\\"; +export declare type ValidationResponse = { + hasError: boolean; + errorMessage?: string; +}; +export declare type ValidationFunction = (value: T, validationResponse: ValidationResponse) => ValidationResponse | Promise; +export declare type MovieUpdateFormInputValues = { + movieKey?: string; + title?: string; + genre?: string; + rating?: string; + tags?: Tag[]; +}; +export declare type MovieUpdateFormValidationValues = { + movieKey?: ValidationFunction; + title?: ValidationFunction; + genre?: ValidationFunction; + rating?: ValidationFunction; + tags?: ValidationFunction; +}; +export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes; +export declare type MovieUpdateFormOverridesProps = { + MovieUpdateFormGrid?: PrimitiveOverrideProps; + movieKey?: PrimitiveOverrideProps; + title?: PrimitiveOverrideProps; + genre?: PrimitiveOverrideProps; + rating?: PrimitiveOverrideProps; + tags?: PrimitiveOverrideProps; +} & EscapeHatchProps; +export declare type MovieUpdateFormProps = React.PropsWithChildren<{ + overrides?: MovieUpdateFormOverridesProps | undefined | null; +} & { + id?: { + movieKey: string; + title: string; + genre: string; + }; + movie?: Movie; + onSubmit?: (fields: MovieUpdateFormInputValues) => MovieUpdateFormInputValues; + onSuccess?: (fields: MovieUpdateFormInputValues) => void; + onError?: (fields: MovieUpdateFormInputValues, errorMessage: string) => void; + onChange?: (fields: MovieUpdateFormInputValues) => MovieUpdateFormInputValues; + onValidate?: MovieUpdateFormValidationValues; +} & React.CSSProperties>; +export default function MovieUpdateForm(props: MovieUpdateFormProps): React.ReactElement; +" +`; + +exports[`amplify form renderer tests GraphQL form tests should generate an update form with hasMany relationship 1`] = ` +"/* eslint-disable */ +import * as React from \\"react\\"; +import { + Autocomplete, + Badge, + Button, + Divider, + Flex, + Grid, + Icon, + ScrollView, + Text, + TextField, + useTheme, +} from \\"@aws-amplify/ui-react\\"; +import { getOverrideProps } from \\"@aws-amplify/ui-react/internal\\"; +import { fetchByPath, validateField } from \\"./utils\\"; +import { API } from \\"aws-amplify\\"; +import { getComment, getPost, listPosts } from \\"../graphql/queries\\"; +import { updateComment } from \\"../graphql/mutations\\"; +function ArrayField({ + items = [], + onChange, + label, + inputFieldRef, + children, + hasError, + setFieldValue, + currentFieldValue, + defaultFieldValue, + lengthLimit, + getBadgeText, + runValidationTasks, + errorMessage, +}) { + const labelElement = {label}; + const { + tokens: { + components: { + fieldmessages: { error: errorStyles }, + }, + }, + } = useTheme(); + const [selectedBadgeIndex, setSelectedBadgeIndex] = React.useState(); + const [isEditing, setIsEditing] = React.useState(); + React.useEffect(() => { + if (isEditing) { + inputFieldRef?.current?.focus(); + } + }, [isEditing]); + const removeItem = async (removeIndex) => { + const newItems = items.filter((value, index) => index !== removeIndex); + await onChange(newItems); + setSelectedBadgeIndex(undefined); + }; + const addItem = async () => { + const { hasError } = runValidationTasks(); + if ( + currentFieldValue !== undefined && + currentFieldValue !== null && + currentFieldValue !== \\"\\" && + !hasError + ) { + const newItems = [...items]; + if (selectedBadgeIndex !== undefined) { + newItems[selectedBadgeIndex] = currentFieldValue; + setSelectedBadgeIndex(undefined); + } else { + newItems.push(currentFieldValue); + } + await onChange(newItems); + setIsEditing(false); + } + }; + const arraySection = ( + + {!!items?.length && ( + + {items.map((value, index) => { + return ( + { + setSelectedBadgeIndex(index); + setFieldValue(items[index]); + setIsEditing(true); + }} + > + {getBadgeText ? getBadgeText(value) : value.toString()} + { + event.stopPropagation(); + removeItem(index); + }} + /> + + ); + })} + + )} + + + ); + if (lengthLimit !== undefined && items.length >= lengthLimit && !isEditing) { + return ( + + {labelElement} + {arraySection} + + ); + } + return ( + + {labelElement} + {isEditing && children} + {!isEditing ? ( + <> + + {errorMessage && hasError && ( + + {errorMessage} + + )} + + ) : ( + + {(currentFieldValue || isEditing) && ( + + )} + + + )} + {arraySection} + + ); +} +export default function CommentUpdateForm(props) { + const { + id: idProp, + comment: commentModelProp, + onSuccess, + onError, + onSubmit, + onValidate, + onChange, + overrides, + ...rest + } = props; + const initialValues = { + content: \\"\\", + postID: undefined, + Post: undefined, + post: \\"\\", + }; + const [content, setContent] = React.useState(initialValues.content); + const [postID, setPostID] = React.useState(initialValues.postID); + const [postIDLoading, setPostIDLoading] = React.useState(false); + const [postIDRecords, setPostIDRecords] = React.useState([]); + const [selectedPostIDRecords, setSelectedPostIDRecords] = React.useState([]); + const [Post, setPost] = React.useState(initialValues.Post); + const [PostLoading, setPostLoading] = React.useState(false); + const [PostRecords, setPostRecords] = React.useState([]); + const [post1, setPost1] = React.useState(initialValues.post); + const autocompleteLength = 10; + const [errors, setErrors] = React.useState({}); + const resetStateValues = () => { + const cleanValues = commentRecord + ? { ...initialValues, ...commentRecord, postID, Post } + : initialValues; + setContent(cleanValues.content); + setPostID(cleanValues.postID); + setCurrentPostIDValue(undefined); + setCurrentPostIDDisplayValue(\\"\\"); + setPost(cleanValues.Post); + setCurrentPostValue(undefined); + setCurrentPostDisplayValue(\\"\\"); + setPost1(cleanValues.post); + setErrors({}); + }; + const [commentRecord, setCommentRecord] = React.useState(commentModelProp); + React.useEffect(() => { + const queryData = async () => { + const record = idProp + ? ( + await API.graphql({ + query: getComment.replaceAll(\\"__typename\\", \\"\\"), + variables: { id: idProp }, + }) + )?.data?.getComment + : commentModelProp; + const postIDRecord = record ? record.postID : undefined; + const postRecord = postIDRecord + ? ( + await API.graphql({ + query: getPost.replaceAll(\\"__typename\\", \\"\\"), + variables: { id: postIDRecord }, + }) + )?.data?.getPost + : undefined; + setPostID(postIDRecord); + setSelectedPostIDRecords([postRecord]); + const PostRecord = record ? await record.Post : undefined; + setPost(PostRecord); + setCommentRecord(record); + }; + queryData(); + }, [idProp, commentModelProp]); + React.useEffect(resetStateValues, [commentRecord, postID, Post]); + const [currentPostIDDisplayValue, setCurrentPostIDDisplayValue] = + React.useState(\\"\\"); + const [currentPostIDValue, setCurrentPostIDValue] = React.useState(undefined); + const postIDRef = React.createRef(); + const [currentPostDisplayValue, setCurrentPostDisplayValue] = + React.useState(\\"\\"); + const [currentPostValue, setCurrentPostValue] = React.useState(undefined); + const PostRef = React.createRef(); + const getIDValue = { + Post: (r) => JSON.stringify({ id: r?.id }), + }; + const PostIdSet = new Set( + Array.isArray(Post) + ? Post.map((r) => getIDValue.Post?.(r)) + : getIDValue.Post?.(Post) + ); + const getDisplayValue = { + postID: (r) => \`\${r?.title ? r?.title + \\" - \\" : \\"\\"}\${r?.id}\`, + Post: (r) => \`\${r?.title ? r?.title + \\" - \\" : \\"\\"}\${r?.id}\`, + }; + const validations = { + content: [], + postID: [{ type: \\"Required\\" }], + Post: [], + post: [], + }; + const runValidationTasks = async ( + fieldName, + currentValue, + getDisplayValue + ) => { + const value = + currentValue && getDisplayValue + ? getDisplayValue(currentValue) + : currentValue; + let validationResponse = validateField(value, validations[fieldName]); + const customValidator = fetchByPath(onValidate, fieldName); + if (customValidator) { + validationResponse = await customValidator(value, validationResponse); + } + setErrors((errors) => ({ ...errors, [fieldName]: validationResponse })); + return validationResponse; + }; + const fetchPostIDRecords = async (value) => { + setPostIDLoading(true); + const newOptions = []; + let newNext = \\"\\"; + while (newOptions.length < autocompleteLength && newNext != null) { + const variables = { + limit: autocompleteLength * 5, + filter: { + or: [{ title: { contains: value } }, { id: { contains: value } }], + }, + }; + if (newNext) { + variables[\\"nextToken\\"] = newNext; + } + const result = ( + await API.graphql({ + query: listPosts.replaceAll(\\"__typename\\", \\"\\"), + variables, + }) + )?.data?.listPosts?.items; + var loaded = result.filter((item) => postID !== item.id); + newOptions.push(...loaded); + newNext = result.nextToken; + } + setPostIDRecords(newOptions.slice(0, autocompleteLength)); + setPostIDLoading(false); + }; + const fetchPostRecords = async (value) => { + setPostLoading(true); + const newOptions = []; + let newNext = \\"\\"; + while (newOptions.length < autocompleteLength && newNext != null) { + const variables = { + limit: autocompleteLength * 5, + filter: { + or: [{ title: { contains: value } }, { id: { contains: value } }], + }, + }; + if (newNext) { + variables[\\"nextToken\\"] = newNext; + } + const result = ( + await API.graphql({ + query: listPosts.replaceAll(\\"__typename\\", \\"\\"), + variables, + }) + )?.data?.listPosts?.items; + var loaded = result.filter( + (item) => !PostIdSet.has(getIDValue.Post?.(item)) + ); + newOptions.push(...loaded); + newNext = result.nextToken; + } + setPostRecords(newOptions.slice(0, autocompleteLength)); + setPostLoading(false); + }; + React.useEffect(() => { + fetchPostIDRecords(\\"\\"); + fetchPostRecords(\\"\\"); + }, []); + return ( + { + event.preventDefault(); + let modelFields = { + content: content ?? null, + postID, + Post: Post ?? null, + post: post ?? null, + }; + const validationResponses = await Promise.all( + Object.keys(validations).reduce((promises, fieldName) => { + if (Array.isArray(modelFields[fieldName])) { + promises.push( + ...modelFields[fieldName].map((item) => + runValidationTasks( + fieldName, + item, + getDisplayValue[fieldName] + ) + ) + ); + return promises; + } + promises.push( + runValidationTasks( + fieldName, + modelFields[fieldName], + getDisplayValue[fieldName] + ) + ); + return promises; + }, []) + ); + if (validationResponses.some((r) => r.hasError)) { + return; + } + if (onSubmit) { + modelFields = onSubmit(modelFields); + } + try { + Object.entries(modelFields).forEach(([key, value]) => { + if (typeof value === \\"string\\" && value === \\"\\") { + modelFields[key] = null; + } + }); + const modelFieldsToSave = { + content: modelFields.content ?? null, + postID: modelFields.postID, + postCommentsId: modelFields?.Post?.id ?? null, + }; + await API.graphql({ + query: updateComment.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + id: commentRecord.id, + ...modelFieldsToSave, + }, + }, + }); + if (onSuccess) { + onSuccess(modelFields); + } + } catch (err) { + if (onError) { + const messages = err.errors.map((e) => e.message).join(\\"\\\\n\\"); + onError(modelFields, messages); + } + } + }} + {...getOverrideProps(overrides, \\"CommentUpdateForm\\")} + {...rest} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + content: value, + postID, + Post, + post: post1, + }; + const result = onChange(modelFields); + value = result?.content ?? value; + } + if (errors.content?.hasError) { + runValidationTasks(\\"content\\", value); + } + setContent(value); + }} + onBlur={() => runValidationTasks(\\"content\\", content)} + errorMessage={errors.content?.errorMessage} + hasError={errors.content?.hasError} + {...getOverrideProps(overrides, \\"content\\")} + > + { + let value = items[0]; + if (onChange) { + const modelFields = { + content, + postID: value, + Post, + post: post1, + }; + const result = onChange(modelFields); + value = result?.postID ?? value; + } + setPostID(value); + setCurrentPostIDValue(undefined); + }} + currentFieldValue={currentPostIDValue} + label={\\"Post id\\"} + items={postID ? [postID] : []} + hasError={errors?.postID?.hasError} + runValidationTasks={async () => + await runValidationTasks(\\"postID\\", currentPostIDValue) + } + errorMessage={errors?.postID?.errorMessage} + getBadgeText={(value) => + value + ? getDisplayValue.postID( + postIDRecords.find((r) => r.id === value) ?? + selectedPostIDRecords.find((r) => r.id === value) + ) + : \\"\\" + } + setFieldValue={(value) => { + setCurrentPostIDDisplayValue( + value + ? getDisplayValue.postID( + postIDRecords.find((r) => r.id === value) ?? + selectedPostIDRecords.find((r) => r.id === value) + ) + : \\"\\" + ); + setCurrentPostIDValue(value); + const selectedRecord = postIDRecords.find((r) => r.id === value); + if (selectedRecord) { + setSelectedPostIDRecords([selectedRecord]); + } + }} + inputFieldRef={postIDRef} + defaultFieldValue={\\"\\"} + > + + arr.findIndex((member) => member?.id === r?.id) === i + ) + .map((r) => ({ + id: r?.id, + label: getDisplayValue.postID?.(r), + }))} + isLoading={postIDLoading} + onSelect={({ id, label }) => { + setCurrentPostIDValue(id); + setCurrentPostIDDisplayValue(label); + runValidationTasks(\\"postID\\", label); + }} + onClear={() => { + setCurrentPostIDDisplayValue(\\"\\"); + }} + defaultValue={postID} + onChange={(e) => { + let { value } = e.target; + fetchPostIDRecords(value); + if (errors.postID?.hasError) { + runValidationTasks(\\"postID\\", value); + } + setCurrentPostIDDisplayValue(value); + setCurrentPostIDValue(undefined); + }} + onBlur={() => runValidationTasks(\\"postID\\", currentPostIDValue)} + errorMessage={errors.postID?.errorMessage} + hasError={errors.postID?.hasError} + ref={postIDRef} + labelHidden={true} + {...getOverrideProps(overrides, \\"postID\\")} + > + + { + let value = items[0]; + if (onChange) { + const modelFields = { + content, + postID, + Post: value, + post: post1, + }; + const result = onChange(modelFields); + value = result?.Post ?? value; + } + setPost(value); + setCurrentPostValue(undefined); + setCurrentPostDisplayValue(\\"\\"); + }} + currentFieldValue={currentPostValue} + label={\\"Post\\"} + items={Post ? [Post] : []} + hasError={errors?.Post?.hasError} + runValidationTasks={async () => + await runValidationTasks(\\"Post\\", currentPostValue) + } + errorMessage={errors?.Post?.errorMessage} + getBadgeText={getDisplayValue.Post} + setFieldValue={(model) => { + setCurrentPostDisplayValue(model ? getDisplayValue.Post(model) : \\"\\"); + setCurrentPostValue(model); + }} + inputFieldRef={PostRef} + defaultFieldValue={\\"\\"} + > + !PostIdSet.has(getIDValue.Post?.(r)) + ).map((r) => ({ + id: getIDValue.Post?.(r), + label: getDisplayValue.Post?.(r), + }))} + isLoading={PostLoading} + onSelect={({ id, label }) => { + setCurrentPostValue( + PostRecords.find((r) => + Object.entries(JSON.parse(id)).every( + ([key, value]) => r[key] === value + ) + ) + ); + setCurrentPostDisplayValue(label); + runValidationTasks(\\"Post\\", label); + }} + onClear={() => { + setCurrentPostDisplayValue(\\"\\"); + }} + defaultValue={Post} + onChange={(e) => { + let { value } = e.target; + fetchPostRecords(value); + if (errors.Post?.hasError) { + runValidationTasks(\\"Post\\", value); + } + setCurrentPostDisplayValue(value); + setCurrentPostValue(undefined); + }} + onBlur={() => runValidationTasks(\\"Post\\", currentPostDisplayValue)} + errorMessage={errors.Post?.errorMessage} + hasError={errors.Post?.hasError} + ref={PostRef} + labelHidden={true} + {...getOverrideProps(overrides, \\"Post\\")} + > + + { + let { value } = e.target; + if (onChange) { + const modelFields = { + content, + postID, + Post, + post: value, + }; + const result = onChange(modelFields); + value = result?.post ?? value; + } + if (errors.post?.hasError) { + runValidationTasks(\\"post\\", value); + } + setPost1(value); + }} + onBlur={() => runValidationTasks(\\"post\\", post1)} + errorMessage={errors.post?.errorMessage} + hasError={errors.post?.hasError} + {...getOverrideProps(overrides, \\"post\\")} + > + + + + + + + + ); +} +" +`; + +exports[`amplify form renderer tests GraphQL form tests should generate an update form with hasMany relationship 2`] = ` +"import * as React from \\"react\\"; +import { AutocompleteProps, GridProps, TextFieldProps } from \\"@aws-amplify/ui-react\\"; +import { EscapeHatchProps } from \\"@aws-amplify/ui-react/internal\\"; +import { Comment, Post } from \\"../API\\"; +export declare type ValidationResponse = { + hasError: boolean; + errorMessage?: string; +}; +export declare type ValidationFunction = (value: T, validationResponse: ValidationResponse) => ValidationResponse | Promise; +export declare type CommentUpdateFormInputValues = { + content?: string; + postID?: string; + Post?: Post; + post?: string; +}; +export declare type CommentUpdateFormValidationValues = { + content?: ValidationFunction; + postID?: ValidationFunction; + Post?: ValidationFunction; + post?: ValidationFunction; +}; +export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes; +export declare type CommentUpdateFormOverridesProps = { + CommentUpdateFormGrid?: PrimitiveOverrideProps; + content?: PrimitiveOverrideProps; + postID?: PrimitiveOverrideProps; + Post?: PrimitiveOverrideProps; + post?: PrimitiveOverrideProps; +} & EscapeHatchProps; +export declare type CommentUpdateFormProps = React.PropsWithChildren<{ + overrides?: CommentUpdateFormOverridesProps | undefined | null; +} & { + id?: string; + comment?: Comment; + onSubmit?: (fields: CommentUpdateFormInputValues) => CommentUpdateFormInputValues; + onSuccess?: (fields: CommentUpdateFormInputValues) => void; + onError?: (fields: CommentUpdateFormInputValues, errorMessage: string) => void; + onChange?: (fields: CommentUpdateFormInputValues) => CommentUpdateFormInputValues; + onValidate?: CommentUpdateFormValidationValues; +} & React.CSSProperties>; +export default function CommentUpdateForm(props: CommentUpdateFormProps): React.ReactElement; +" +`; + +exports[`amplify form renderer tests GraphQL form tests should generate an update form with hasMany relationship without types file - amplify js v6 1`] = ` +"/* eslint-disable */ +import * as React from \\"react\\"; +import { + Autocomplete, + Badge, + Button, + Divider, + Flex, + Grid, + Icon, + ScrollView, + Text, + TextField, + useTheme, +} from \\"@aws-amplify/ui-react\\"; +import { getOverrideProps } from \\"@aws-amplify/ui-react/internal\\"; +import { fetchByPath, validateField } from \\"./utils\\"; +import { generateClient } from \\"aws-amplify/api\\"; +import { getComment, getPost, listPosts } from \\"../graphql/queries\\"; +import { updateComment } from \\"../graphql/mutations\\"; +const client = generateClient(); +function ArrayField({ + items = [], + onChange, + label, + inputFieldRef, + children, + hasError, + setFieldValue, + currentFieldValue, + defaultFieldValue, + lengthLimit, + getBadgeText, + runValidationTasks, + errorMessage, +}) { + const labelElement = {label}; + const { + tokens: { + components: { + fieldmessages: { error: errorStyles }, + }, + }, + } = useTheme(); + const [selectedBadgeIndex, setSelectedBadgeIndex] = React.useState(); + const [isEditing, setIsEditing] = React.useState(); + React.useEffect(() => { + if (isEditing) { + inputFieldRef?.current?.focus(); + } + }, [isEditing]); + const removeItem = async (removeIndex) => { + const newItems = items.filter((value, index) => index !== removeIndex); + await onChange(newItems); + setSelectedBadgeIndex(undefined); + }; + const addItem = async () => { + const { hasError } = runValidationTasks(); + if ( + currentFieldValue !== undefined && + currentFieldValue !== null && + currentFieldValue !== \\"\\" && + !hasError + ) { + const newItems = [...items]; + if (selectedBadgeIndex !== undefined) { + newItems[selectedBadgeIndex] = currentFieldValue; + setSelectedBadgeIndex(undefined); + } else { + newItems.push(currentFieldValue); + } + await onChange(newItems); + setIsEditing(false); + } + }; + const arraySection = ( + + {!!items?.length && ( + + {items.map((value, index) => { + return ( + { + setSelectedBadgeIndex(index); + setFieldValue(items[index]); + setIsEditing(true); + }} + > + {getBadgeText ? getBadgeText(value) : value.toString()} + { + event.stopPropagation(); + removeItem(index); + }} + /> + + ); + })} + + )} + + + ); + if (lengthLimit !== undefined && items.length >= lengthLimit && !isEditing) { + return ( + + {labelElement} + {arraySection} + + ); + } + return ( + + {labelElement} + {isEditing && children} + {!isEditing ? ( + <> + + {errorMessage && hasError && ( + + {errorMessage} + + )} + + ) : ( + + {(currentFieldValue || isEditing) && ( + + )} + + + )} + {arraySection} + + ); +} +export default function CommentUpdateForm(props) { + const { + id: idProp, + comment: commentModelProp, + onSuccess, + onError, + onSubmit, + onValidate, + onChange, + overrides, + ...rest + } = props; + const initialValues = { + content: \\"\\", + postID: undefined, + Post: undefined, + post: \\"\\", + }; + const [content, setContent] = React.useState(initialValues.content); + const [postID, setPostID] = React.useState(initialValues.postID); + const [postIDLoading, setPostIDLoading] = React.useState(false); + const [postIDRecords, setPostIDRecords] = React.useState([]); + const [selectedPostIDRecords, setSelectedPostIDRecords] = React.useState([]); + const [Post, setPost] = React.useState(initialValues.Post); + const [PostLoading, setPostLoading] = React.useState(false); + const [PostRecords, setPostRecords] = React.useState([]); + const [post1, setPost1] = React.useState(initialValues.post); + const autocompleteLength = 10; + const [errors, setErrors] = React.useState({}); + const resetStateValues = () => { + const cleanValues = commentRecord + ? { ...initialValues, ...commentRecord, postID, Post } + : initialValues; + setContent(cleanValues.content); + setPostID(cleanValues.postID); + setCurrentPostIDValue(undefined); + setCurrentPostIDDisplayValue(\\"\\"); + setPost(cleanValues.Post); + setCurrentPostValue(undefined); + setCurrentPostDisplayValue(\\"\\"); + setPost1(cleanValues.post); + setErrors({}); + }; + const [commentRecord, setCommentRecord] = React.useState(commentModelProp); + React.useEffect(() => { + const queryData = async () => { + const record = idProp + ? ( + await client.graphql({ + query: getComment.replaceAll(\\"__typename\\", \\"\\"), + variables: { id: idProp }, + }) + )?.data?.getComment + : commentModelProp; + const postIDRecord = record ? record.postID : undefined; + const postRecord = postIDRecord + ? ( + await client.graphql({ + query: getPost.replaceAll(\\"__typename\\", \\"\\"), + variables: { id: postIDRecord }, + }) + )?.data?.getPost + : undefined; + setPostID(postIDRecord); + setSelectedPostIDRecords([postRecord]); + const PostRecord = record ? await record.Post : undefined; + setPost(PostRecord); + setCommentRecord(record); + }; + queryData(); + }, [idProp, commentModelProp]); + React.useEffect(resetStateValues, [commentRecord, postID, Post]); + const [currentPostIDDisplayValue, setCurrentPostIDDisplayValue] = + React.useState(\\"\\"); + const [currentPostIDValue, setCurrentPostIDValue] = React.useState(undefined); + const postIDRef = React.createRef(); + const [currentPostDisplayValue, setCurrentPostDisplayValue] = + React.useState(\\"\\"); + const [currentPostValue, setCurrentPostValue] = React.useState(undefined); + const PostRef = React.createRef(); + const getIDValue = { + Post: (r) => JSON.stringify({ id: r?.id }), + }; + const PostIdSet = new Set( + Array.isArray(Post) + ? Post.map((r) => getIDValue.Post?.(r)) + : getIDValue.Post?.(Post) + ); + const getDisplayValue = { + postID: (r) => \`\${r?.title ? r?.title + \\" - \\" : \\"\\"}\${r?.id}\`, + Post: (r) => \`\${r?.title ? r?.title + \\" - \\" : \\"\\"}\${r?.id}\`, + }; + const validations = { + content: [], + postID: [{ type: \\"Required\\" }], + Post: [], + post: [], + }; + const runValidationTasks = async ( + fieldName, + currentValue, + getDisplayValue + ) => { + const value = + currentValue && getDisplayValue + ? getDisplayValue(currentValue) + : currentValue; + let validationResponse = validateField(value, validations[fieldName]); + const customValidator = fetchByPath(onValidate, fieldName); + if (customValidator) { + validationResponse = await customValidator(value, validationResponse); + } + setErrors((errors) => ({ ...errors, [fieldName]: validationResponse })); + return validationResponse; + }; + const fetchPostIDRecords = async (value) => { + setPostIDLoading(true); + const newOptions = []; + let newNext = \\"\\"; + while (newOptions.length < autocompleteLength && newNext != null) { + const variables = { + limit: autocompleteLength * 5, + filter: { + or: [{ title: { contains: value } }, { id: { contains: value } }], + }, + }; + if (newNext) { + variables[\\"nextToken\\"] = newNext; + } + const result = ( + await client.graphql({ + query: listPosts.replaceAll(\\"__typename\\", \\"\\"), + variables, + }) + )?.data?.listPosts?.items; + var loaded = result.filter((item) => postID !== item.id); + newOptions.push(...loaded); + newNext = result.nextToken; + } + setPostIDRecords(newOptions.slice(0, autocompleteLength)); + setPostIDLoading(false); + }; + const fetchPostRecords = async (value) => { + setPostLoading(true); + const newOptions = []; + let newNext = \\"\\"; + while (newOptions.length < autocompleteLength && newNext != null) { + const variables = { + limit: autocompleteLength * 5, + filter: { + or: [{ title: { contains: value } }, { id: { contains: value } }], + }, + }; + if (newNext) { + variables[\\"nextToken\\"] = newNext; + } + const result = ( + await client.graphql({ + query: listPosts.replaceAll(\\"__typename\\", \\"\\"), + variables, + }) + )?.data?.listPosts?.items; + var loaded = result.filter( + (item) => !PostIdSet.has(getIDValue.Post?.(item)) + ); + newOptions.push(...loaded); + newNext = result.nextToken; + } + setPostRecords(newOptions.slice(0, autocompleteLength)); + setPostLoading(false); + }; + React.useEffect(() => { + fetchPostIDRecords(\\"\\"); + fetchPostRecords(\\"\\"); + }, []); + return ( + { + event.preventDefault(); + let modelFields = { + content: content ?? null, + postID, + Post: Post ?? null, + post: post ?? null, + }; + const validationResponses = await Promise.all( + Object.keys(validations).reduce((promises, fieldName) => { + if (Array.isArray(modelFields[fieldName])) { + promises.push( + ...modelFields[fieldName].map((item) => + runValidationTasks( + fieldName, + item, + getDisplayValue[fieldName] + ) + ) + ); + return promises; + } + promises.push( + runValidationTasks( + fieldName, + modelFields[fieldName], + getDisplayValue[fieldName] + ) + ); + return promises; + }, []) + ); + if (validationResponses.some((r) => r.hasError)) { + return; + } + if (onSubmit) { + modelFields = onSubmit(modelFields); + } + try { + Object.entries(modelFields).forEach(([key, value]) => { + if (typeof value === \\"string\\" && value === \\"\\") { + modelFields[key] = null; + } + }); + const modelFieldsToSave = { + content: modelFields.content ?? null, + postID: modelFields.postID, + postCommentsId: modelFields?.Post?.id ?? null, + }; + await client.graphql({ + query: updateComment.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + id: commentRecord.id, + ...modelFieldsToSave, + }, + }, + }); + if (onSuccess) { + onSuccess(modelFields); + } + } catch (err) { + if (onError) { + const messages = err.errors.map((e) => e.message).join(\\"\\\\n\\"); + onError(modelFields, messages); + } + } + }} + {...getOverrideProps(overrides, \\"CommentUpdateForm\\")} + {...rest} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + content: value, + postID, + Post, + post: post1, + }; + const result = onChange(modelFields); + value = result?.content ?? value; + } + if (errors.content?.hasError) { + runValidationTasks(\\"content\\", value); + } + setContent(value); + }} + onBlur={() => runValidationTasks(\\"content\\", content)} + errorMessage={errors.content?.errorMessage} + hasError={errors.content?.hasError} + {...getOverrideProps(overrides, \\"content\\")} + > + { + let value = items[0]; + if (onChange) { + const modelFields = { + content, + postID: value, + Post, + post: post1, + }; + const result = onChange(modelFields); + value = result?.postID ?? value; + } + setPostID(value); + setCurrentPostIDValue(undefined); + }} + currentFieldValue={currentPostIDValue} + label={\\"Post id\\"} + items={postID ? [postID] : []} + hasError={errors?.postID?.hasError} + runValidationTasks={async () => + await runValidationTasks(\\"postID\\", currentPostIDValue) + } + errorMessage={errors?.postID?.errorMessage} + getBadgeText={(value) => + value + ? getDisplayValue.postID( + postIDRecords.find((r) => r.id === value) ?? + selectedPostIDRecords.find((r) => r.id === value) + ) + : \\"\\" + } + setFieldValue={(value) => { + setCurrentPostIDDisplayValue( + value + ? getDisplayValue.postID( + postIDRecords.find((r) => r.id === value) ?? + selectedPostIDRecords.find((r) => r.id === value) + ) + : \\"\\" + ); + setCurrentPostIDValue(value); + const selectedRecord = postIDRecords.find((r) => r.id === value); + if (selectedRecord) { + setSelectedPostIDRecords([selectedRecord]); + } + }} + inputFieldRef={postIDRef} + defaultFieldValue={\\"\\"} + > + + arr.findIndex((member) => member?.id === r?.id) === i + ) + .map((r) => ({ + id: r?.id, + label: getDisplayValue.postID?.(r), + }))} + isLoading={postIDLoading} + onSelect={({ id, label }) => { + setCurrentPostIDValue(id); + setCurrentPostIDDisplayValue(label); + runValidationTasks(\\"postID\\", label); + }} + onClear={() => { + setCurrentPostIDDisplayValue(\\"\\"); + }} + defaultValue={postID} + onChange={(e) => { + let { value } = e.target; + fetchPostIDRecords(value); + if (errors.postID?.hasError) { + runValidationTasks(\\"postID\\", value); + } + setCurrentPostIDDisplayValue(value); + setCurrentPostIDValue(undefined); + }} + onBlur={() => runValidationTasks(\\"postID\\", currentPostIDValue)} + errorMessage={errors.postID?.errorMessage} + hasError={errors.postID?.hasError} + ref={postIDRef} + labelHidden={true} + {...getOverrideProps(overrides, \\"postID\\")} + > + + { + let value = items[0]; + if (onChange) { + const modelFields = { + content, + postID, + Post: value, + post: post1, + }; + const result = onChange(modelFields); + value = result?.Post ?? value; + } + setPost(value); + setCurrentPostValue(undefined); + setCurrentPostDisplayValue(\\"\\"); + }} + currentFieldValue={currentPostValue} + label={\\"Post\\"} + items={Post ? [Post] : []} + hasError={errors?.Post?.hasError} + runValidationTasks={async () => + await runValidationTasks(\\"Post\\", currentPostValue) + } + errorMessage={errors?.Post?.errorMessage} + getBadgeText={getDisplayValue.Post} + setFieldValue={(model) => { + setCurrentPostDisplayValue(model ? getDisplayValue.Post(model) : \\"\\"); + setCurrentPostValue(model); + }} + inputFieldRef={PostRef} + defaultFieldValue={\\"\\"} + > + !PostIdSet.has(getIDValue.Post?.(r)) + ).map((r) => ({ + id: getIDValue.Post?.(r), + label: getDisplayValue.Post?.(r), + }))} + isLoading={PostLoading} + onSelect={({ id, label }) => { + setCurrentPostValue( + PostRecords.find((r) => + Object.entries(JSON.parse(id)).every( + ([key, value]) => r[key] === value + ) + ) + ); + setCurrentPostDisplayValue(label); + runValidationTasks(\\"Post\\", label); + }} + onClear={() => { + setCurrentPostDisplayValue(\\"\\"); + }} + defaultValue={Post} + onChange={(e) => { + let { value } = e.target; + fetchPostRecords(value); + if (errors.Post?.hasError) { + runValidationTasks(\\"Post\\", value); + } + setCurrentPostDisplayValue(value); + setCurrentPostValue(undefined); + }} + onBlur={() => runValidationTasks(\\"Post\\", currentPostDisplayValue)} + errorMessage={errors.Post?.errorMessage} + hasError={errors.Post?.hasError} + ref={PostRef} + labelHidden={true} + {...getOverrideProps(overrides, \\"Post\\")} + > + + { + let { value } = e.target; + if (onChange) { + const modelFields = { + content, + postID, + Post, + post: value, + }; + const result = onChange(modelFields); + value = result?.post ?? value; + } + if (errors.post?.hasError) { + runValidationTasks(\\"post\\", value); + } + setPost1(value); + }} + onBlur={() => runValidationTasks(\\"post\\", post1)} + errorMessage={errors.post?.errorMessage} + hasError={errors.post?.hasError} + {...getOverrideProps(overrides, \\"post\\")} + > + + + + + + + + ); +} +" +`; + +exports[`amplify form renderer tests GraphQL form tests should generate an update form with hasMany relationship without types file - amplify js v6 2`] = ` +"import * as React from \\"react\\"; +import { AutocompleteProps, GridProps, TextFieldProps } from \\"@aws-amplify/ui-react\\"; +import { EscapeHatchProps } from \\"@aws-amplify/ui-react/internal\\"; +export declare type ValidationResponse = { + hasError: boolean; + errorMessage?: string; +}; +export declare type ValidationFunction = (value: T, validationResponse: ValidationResponse) => ValidationResponse | Promise; +export declare type CommentUpdateFormInputValues = { + content?: string; + postID?: string; + Post?: any; + post?: string; +}; +export declare type CommentUpdateFormValidationValues = { + content?: ValidationFunction; + postID?: ValidationFunction; + Post?: ValidationFunction; + post?: ValidationFunction; +}; +export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes; +export declare type CommentUpdateFormOverridesProps = { + CommentUpdateFormGrid?: PrimitiveOverrideProps; + content?: PrimitiveOverrideProps; + postID?: PrimitiveOverrideProps; + Post?: PrimitiveOverrideProps; + post?: PrimitiveOverrideProps; +} & EscapeHatchProps; +export declare type CommentUpdateFormProps = React.PropsWithChildren<{ + overrides?: CommentUpdateFormOverridesProps | undefined | null; +} & { + id?: string; + comment?: any; + onSubmit?: (fields: CommentUpdateFormInputValues) => CommentUpdateFormInputValues; + onSuccess?: (fields: CommentUpdateFormInputValues) => void; + onError?: (fields: CommentUpdateFormInputValues, errorMessage: string) => void; + onChange?: (fields: CommentUpdateFormInputValues) => CommentUpdateFormInputValues; + onValidate?: CommentUpdateFormValidationValues; +} & React.CSSProperties>; +export default function CommentUpdateForm(props: CommentUpdateFormProps): React.ReactElement; +" +`; + +exports[`amplify form renderer tests GraphQL form tests should generate an update form with hasMany relationship without types file 1`] = ` +"/* eslint-disable */ +import * as React from \\"react\\"; +import { + Autocomplete, + Badge, + Button, + Divider, + Flex, + Grid, + Icon, + ScrollView, + Text, + TextField, + useTheme, +} from \\"@aws-amplify/ui-react\\"; +import { getOverrideProps } from \\"@aws-amplify/ui-react/internal\\"; +import { fetchByPath, validateField } from \\"./utils\\"; +import { API } from \\"aws-amplify\\"; +import { getComment, getPost, listPosts } from \\"../graphql/queries\\"; +import { updateComment } from \\"../graphql/mutations\\"; +function ArrayField({ + items = [], + onChange, + label, + inputFieldRef, + children, + hasError, + setFieldValue, + currentFieldValue, + defaultFieldValue, + lengthLimit, + getBadgeText, + runValidationTasks, + errorMessage, +}) { + const labelElement = {label}; + const { + tokens: { + components: { + fieldmessages: { error: errorStyles }, + }, + }, + } = useTheme(); + const [selectedBadgeIndex, setSelectedBadgeIndex] = React.useState(); + const [isEditing, setIsEditing] = React.useState(); + React.useEffect(() => { + if (isEditing) { + inputFieldRef?.current?.focus(); + } + }, [isEditing]); + const removeItem = async (removeIndex) => { + const newItems = items.filter((value, index) => index !== removeIndex); + await onChange(newItems); + setSelectedBadgeIndex(undefined); + }; + const addItem = async () => { + const { hasError } = runValidationTasks(); + if ( + currentFieldValue !== undefined && + currentFieldValue !== null && + currentFieldValue !== \\"\\" && + !hasError + ) { + const newItems = [...items]; + if (selectedBadgeIndex !== undefined) { + newItems[selectedBadgeIndex] = currentFieldValue; + setSelectedBadgeIndex(undefined); + } else { + newItems.push(currentFieldValue); + } + await onChange(newItems); + setIsEditing(false); + } + }; + const arraySection = ( + + {!!items?.length && ( + + {items.map((value, index) => { + return ( + { + setSelectedBadgeIndex(index); + setFieldValue(items[index]); + setIsEditing(true); + }} + > + {getBadgeText ? getBadgeText(value) : value.toString()} + { + event.stopPropagation(); + removeItem(index); + }} + /> + + ); + })} + + )} + + + ); + if (lengthLimit !== undefined && items.length >= lengthLimit && !isEditing) { + return ( + + {labelElement} + {arraySection} + + ); + } + return ( + + {labelElement} + {isEditing && children} + {!isEditing ? ( + <> + + {errorMessage && hasError && ( + + {errorMessage} + + )} + + ) : ( + + {(currentFieldValue || isEditing) && ( + + )} + + + )} + {arraySection} + + ); +} +export default function CommentUpdateForm(props) { + const { + id: idProp, + comment: commentModelProp, + onSuccess, + onError, + onSubmit, + onValidate, + onChange, + overrides, + ...rest + } = props; + const initialValues = { + content: \\"\\", + postID: undefined, + Post: undefined, + post: \\"\\", + }; + const [content, setContent] = React.useState(initialValues.content); + const [postID, setPostID] = React.useState(initialValues.postID); + const [postIDLoading, setPostIDLoading] = React.useState(false); + const [postIDRecords, setPostIDRecords] = React.useState([]); + const [selectedPostIDRecords, setSelectedPostIDRecords] = React.useState([]); + const [Post, setPost] = React.useState(initialValues.Post); + const [PostLoading, setPostLoading] = React.useState(false); + const [PostRecords, setPostRecords] = React.useState([]); + const [post1, setPost1] = React.useState(initialValues.post); + const autocompleteLength = 10; + const [errors, setErrors] = React.useState({}); + const resetStateValues = () => { + const cleanValues = commentRecord + ? { ...initialValues, ...commentRecord, postID, Post } + : initialValues; + setContent(cleanValues.content); + setPostID(cleanValues.postID); + setCurrentPostIDValue(undefined); + setCurrentPostIDDisplayValue(\\"\\"); + setPost(cleanValues.Post); + setCurrentPostValue(undefined); + setCurrentPostDisplayValue(\\"\\"); + setPost1(cleanValues.post); + setErrors({}); + }; + const [commentRecord, setCommentRecord] = React.useState(commentModelProp); + React.useEffect(() => { + const queryData = async () => { + const record = idProp + ? ( + await API.graphql({ + query: getComment.replaceAll(\\"__typename\\", \\"\\"), + variables: { id: idProp }, + }) + )?.data?.getComment + : commentModelProp; + const postIDRecord = record ? record.postID : undefined; + const postRecord = postIDRecord + ? ( + await API.graphql({ + query: getPost.replaceAll(\\"__typename\\", \\"\\"), + variables: { id: postIDRecord }, + }) + )?.data?.getPost + : undefined; + setPostID(postIDRecord); + setSelectedPostIDRecords([postRecord]); + const PostRecord = record ? await record.Post : undefined; + setPost(PostRecord); + setCommentRecord(record); + }; + queryData(); + }, [idProp, commentModelProp]); + React.useEffect(resetStateValues, [commentRecord, postID, Post]); + const [currentPostIDDisplayValue, setCurrentPostIDDisplayValue] = + React.useState(\\"\\"); + const [currentPostIDValue, setCurrentPostIDValue] = React.useState(undefined); + const postIDRef = React.createRef(); + const [currentPostDisplayValue, setCurrentPostDisplayValue] = + React.useState(\\"\\"); + const [currentPostValue, setCurrentPostValue] = React.useState(undefined); + const PostRef = React.createRef(); + const getIDValue = { + Post: (r) => JSON.stringify({ id: r?.id }), + }; + const PostIdSet = new Set( + Array.isArray(Post) + ? Post.map((r) => getIDValue.Post?.(r)) + : getIDValue.Post?.(Post) + ); + const getDisplayValue = { + postID: (r) => \`\${r?.title ? r?.title + \\" - \\" : \\"\\"}\${r?.id}\`, + Post: (r) => \`\${r?.title ? r?.title + \\" - \\" : \\"\\"}\${r?.id}\`, + }; + const validations = { + content: [], + postID: [{ type: \\"Required\\" }], + Post: [], + post: [], + }; + const runValidationTasks = async ( + fieldName, + currentValue, + getDisplayValue + ) => { + const value = + currentValue && getDisplayValue + ? getDisplayValue(currentValue) + : currentValue; + let validationResponse = validateField(value, validations[fieldName]); + const customValidator = fetchByPath(onValidate, fieldName); + if (customValidator) { + validationResponse = await customValidator(value, validationResponse); + } + setErrors((errors) => ({ ...errors, [fieldName]: validationResponse })); + return validationResponse; + }; + const fetchPostIDRecords = async (value) => { + setPostIDLoading(true); + const newOptions = []; + let newNext = \\"\\"; + while (newOptions.length < autocompleteLength && newNext != null) { + const variables = { + limit: autocompleteLength * 5, + filter: { + or: [{ title: { contains: value } }, { id: { contains: value } }], + }, + }; + if (newNext) { + variables[\\"nextToken\\"] = newNext; + } + const result = ( + await API.graphql({ + query: listPosts.replaceAll(\\"__typename\\", \\"\\"), + variables, + }) + )?.data?.listPosts?.items; + var loaded = result.filter((item) => postID !== item.id); + newOptions.push(...loaded); + newNext = result.nextToken; + } + setPostIDRecords(newOptions.slice(0, autocompleteLength)); + setPostIDLoading(false); + }; + const fetchPostRecords = async (value) => { + setPostLoading(true); + const newOptions = []; + let newNext = \\"\\"; + while (newOptions.length < autocompleteLength && newNext != null) { + const variables = { + limit: autocompleteLength * 5, + filter: { + or: [{ title: { contains: value } }, { id: { contains: value } }], + }, + }; + if (newNext) { + variables[\\"nextToken\\"] = newNext; + } + const result = ( + await API.graphql({ + query: listPosts.replaceAll(\\"__typename\\", \\"\\"), + variables, + }) + )?.data?.listPosts?.items; + var loaded = result.filter( + (item) => !PostIdSet.has(getIDValue.Post?.(item)) + ); + newOptions.push(...loaded); + newNext = result.nextToken; + } + setPostRecords(newOptions.slice(0, autocompleteLength)); + setPostLoading(false); + }; + React.useEffect(() => { + fetchPostIDRecords(\\"\\"); + fetchPostRecords(\\"\\"); + }, []); + return ( + { + event.preventDefault(); + let modelFields = { + content: content ?? null, + postID, + Post: Post ?? null, + post: post ?? null, + }; + const validationResponses = await Promise.all( + Object.keys(validations).reduce((promises, fieldName) => { + if (Array.isArray(modelFields[fieldName])) { + promises.push( + ...modelFields[fieldName].map((item) => + runValidationTasks( + fieldName, + item, + getDisplayValue[fieldName] + ) + ) + ); + return promises; + } + promises.push( + runValidationTasks( + fieldName, + modelFields[fieldName], + getDisplayValue[fieldName] + ) + ); + return promises; + }, []) + ); + if (validationResponses.some((r) => r.hasError)) { + return; + } + if (onSubmit) { + modelFields = onSubmit(modelFields); + } + try { + Object.entries(modelFields).forEach(([key, value]) => { + if (typeof value === \\"string\\" && value === \\"\\") { + modelFields[key] = null; + } + }); + const modelFieldsToSave = { + content: modelFields.content ?? null, + postID: modelFields.postID, + postCommentsId: modelFields?.Post?.id ?? null, + }; + await API.graphql({ + query: updateComment.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + id: commentRecord.id, + ...modelFieldsToSave, + }, + }, + }); + if (onSuccess) { + onSuccess(modelFields); + } + } catch (err) { + if (onError) { + const messages = err.errors.map((e) => e.message).join(\\"\\\\n\\"); + onError(modelFields, messages); + } + } + }} + {...getOverrideProps(overrides, \\"CommentUpdateForm\\")} + {...rest} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + content: value, postID, Post, post: post1, @@ -11816,11 +14066,10 @@ export default function CommentUpdateForm(props) { " `; -exports[`amplify form renderer tests GraphQL form tests should generate an update form with hasMany relationship 2`] = ` +exports[`amplify form renderer tests GraphQL form tests should generate an update form with hasMany relationship without types file 2`] = ` "import * as React from \\"react\\"; import { AutocompleteProps, GridProps, TextFieldProps } from \\"@aws-amplify/ui-react\\"; import { EscapeHatchProps } from \\"@aws-amplify/ui-react/internal\\"; -import { Comment, Post } from \\"../API\\"; export declare type ValidationResponse = { hasError: boolean; errorMessage?: string; @@ -11829,13 +14078,13 @@ export declare type ValidationFunction = (value: T, validationResponse: Valid export declare type CommentUpdateFormInputValues = { content?: string; postID?: string; - Post?: Post; + Post?: any; post?: string; }; export declare type CommentUpdateFormValidationValues = { content?: ValidationFunction; postID?: ValidationFunction; - Post?: ValidationFunction; + Post?: ValidationFunction; post?: ValidationFunction; }; export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes; @@ -11850,7 +14099,7 @@ export declare type CommentUpdateFormProps = React.PropsWithChildren<{ overrides?: CommentUpdateFormOverridesProps | undefined | null; } & { id?: string; - comment?: Comment; + comment?: any; onSubmit?: (fields: CommentUpdateFormInputValues) => CommentUpdateFormInputValues; onSuccess?: (fields: CommentUpdateFormInputValues) => void; onError?: (fields: CommentUpdateFormInputValues, errorMessage: string) => void; @@ -11861,7 +14110,7 @@ export default function CommentUpdateForm(props: CommentUpdateFormProps): React. " `; -exports[`amplify form renderer tests GraphQL form tests should generate an update form with hasMany relationship without types file - amplify js v6 1`] = ` +exports[`amplify form renderer tests GraphQL form tests should generate an update form with id field instead of belongsTo 1`] = ` "/* eslint-disable */ import * as React from \\"react\\"; import { @@ -11879,10 +14128,9 @@ import { } from \\"@aws-amplify/ui-react\\"; import { getOverrideProps } from \\"@aws-amplify/ui-react/internal\\"; import { fetchByPath, validateField } from \\"./utils\\"; -import { generateClient } from \\"aws-amplify/api\\"; +import { API } from \\"aws-amplify\\"; import { getComment, getPost, listPosts } from \\"../graphql/queries\\"; import { updateComment } from \\"../graphql/mutations\\"; -const client = generateClient(); function ArrayField({ items = [], onChange, @@ -12053,32 +14301,22 @@ export default function CommentUpdateForm(props) { const initialValues = { content: \\"\\", postID: undefined, - Post: undefined, - post: \\"\\", }; const [content, setContent] = React.useState(initialValues.content); const [postID, setPostID] = React.useState(initialValues.postID); const [postIDLoading, setPostIDLoading] = React.useState(false); const [postIDRecords, setPostIDRecords] = React.useState([]); const [selectedPostIDRecords, setSelectedPostIDRecords] = React.useState([]); - const [Post, setPost] = React.useState(initialValues.Post); - const [PostLoading, setPostLoading] = React.useState(false); - const [PostRecords, setPostRecords] = React.useState([]); - const [post1, setPost1] = React.useState(initialValues.post); const autocompleteLength = 10; const [errors, setErrors] = React.useState({}); const resetStateValues = () => { const cleanValues = commentRecord - ? { ...initialValues, ...commentRecord, postID, Post } + ? { ...initialValues, ...commentRecord, postID } : initialValues; setContent(cleanValues.content); setPostID(cleanValues.postID); setCurrentPostIDValue(undefined); setCurrentPostIDDisplayValue(\\"\\"); - setPost(cleanValues.Post); - setCurrentPostValue(undefined); - setCurrentPostDisplayValue(\\"\\"); - setPost1(cleanValues.post); setErrors({}); }; const [commentRecord, setCommentRecord] = React.useState(commentModelProp); @@ -12086,7 +14324,7 @@ export default function CommentUpdateForm(props) { const queryData = async () => { const record = idProp ? ( - await client.graphql({ + await API.graphql({ query: getComment.replaceAll(\\"__typename\\", \\"\\"), variables: { id: idProp }, }) @@ -12095,7 +14333,7 @@ export default function CommentUpdateForm(props) { const postIDRecord = record ? record.postID : undefined; const postRecord = postIDRecord ? ( - await client.graphql({ + await API.graphql({ query: getPost.replaceAll(\\"__typename\\", \\"\\"), variables: { id: postIDRecord }, }) @@ -12103,38 +14341,21 @@ export default function CommentUpdateForm(props) { : undefined; setPostID(postIDRecord); setSelectedPostIDRecords([postRecord]); - const PostRecord = record ? await record.Post : undefined; - setPost(PostRecord); setCommentRecord(record); }; queryData(); }, [idProp, commentModelProp]); - React.useEffect(resetStateValues, [commentRecord, postID, Post]); + React.useEffect(resetStateValues, [commentRecord, postID]); const [currentPostIDDisplayValue, setCurrentPostIDDisplayValue] = React.useState(\\"\\"); const [currentPostIDValue, setCurrentPostIDValue] = React.useState(undefined); const postIDRef = React.createRef(); - const [currentPostDisplayValue, setCurrentPostDisplayValue] = - React.useState(\\"\\"); - const [currentPostValue, setCurrentPostValue] = React.useState(undefined); - const PostRef = React.createRef(); - const getIDValue = { - Post: (r) => JSON.stringify({ id: r?.id }), - }; - const PostIdSet = new Set( - Array.isArray(Post) - ? Post.map((r) => getIDValue.Post?.(r)) - : getIDValue.Post?.(Post) - ); const getDisplayValue = { postID: (r) => \`\${r?.title ? r?.title + \\" - \\" : \\"\\"}\${r?.id}\`, - Post: (r) => \`\${r?.title ? r?.title + \\" - \\" : \\"\\"}\${r?.id}\`, }; const validations = { - content: [], - postID: [{ type: \\"Required\\" }], - Post: [], - post: [], + content: [{ type: \\"Required\\" }], + postID: [], }; const runValidationTasks = async ( fieldName, @@ -12168,7 +14389,7 @@ export default function CommentUpdateForm(props) { variables[\\"nextToken\\"] = newNext; } const result = ( - await client.graphql({ + await API.graphql({ query: listPosts.replaceAll(\\"__typename\\", \\"\\"), variables, }) @@ -12180,38 +14401,8 @@ export default function CommentUpdateForm(props) { setPostIDRecords(newOptions.slice(0, autocompleteLength)); setPostIDLoading(false); }; - const fetchPostRecords = async (value) => { - setPostLoading(true); - const newOptions = []; - let newNext = \\"\\"; - while (newOptions.length < autocompleteLength && newNext != null) { - const variables = { - limit: autocompleteLength * 5, - filter: { - or: [{ title: { contains: value } }, { id: { contains: value } }], - }, - }; - if (newNext) { - variables[\\"nextToken\\"] = newNext; - } - const result = ( - await client.graphql({ - query: listPosts.replaceAll(\\"__typename\\", \\"\\"), - variables, - }) - )?.data?.listPosts?.items; - var loaded = result.filter( - (item) => !PostIdSet.has(getIDValue.Post?.(item)) - ); - newOptions.push(...loaded); - newNext = result.nextToken; - } - setPostRecords(newOptions.slice(0, autocompleteLength)); - setPostLoading(false); - }; React.useEffect(() => { fetchPostIDRecords(\\"\\"); - fetchPostRecords(\\"\\"); }, []); return ( { event.preventDefault(); let modelFields = { - content: content ?? null, - postID, - Post: Post ?? null, - post: post ?? null, + content, + postID: postID ?? null, }; const validationResponses = await Promise.all( Object.keys(validations).reduce((promises, fieldName) => { if (Array.isArray(modelFields[fieldName])) { promises.push( ...modelFields[fieldName].map((item) => - runValidationTasks( - fieldName, - item, - getDisplayValue[fieldName] - ) + runValidationTasks(fieldName, item) ) ); return promises; } promises.push( - runValidationTasks( - fieldName, - modelFields[fieldName], - getDisplayValue[fieldName] - ) + runValidationTasks(fieldName, modelFields[fieldName]) ); return promises; }, []) @@ -12263,17 +14444,12 @@ export default function CommentUpdateForm(props) { modelFields[key] = null; } }); - const modelFieldsToSave = { - content: modelFields.content ?? null, - postID: modelFields.postID, - postCommentsId: modelFields?.Post?.id ?? null, - }; - await client.graphql({ + await API.graphql({ query: updateComment.replaceAll(\\"__typename\\", \\"\\"), variables: { input: { id: commentRecord.id, - ...modelFieldsToSave, + ...modelFields, }, }, }); @@ -12292,7 +14468,7 @@ export default function CommentUpdateForm(props) { > { @@ -12301,8 +14477,6 @@ export default function CommentUpdateForm(props) { const modelFields = { content: value, postID, - Post, - post: post1, }; const result = onChange(modelFields); value = result?.content ?? value; @@ -12325,8 +14499,6 @@ export default function CommentUpdateForm(props) { const modelFields = { content, postID: value, - Post, - post: post1, }; const result = onChange(modelFields); value = result?.postID ?? value; @@ -12370,7 +14542,7 @@ export default function CommentUpdateForm(props) { > - { - let value = items[0]; - if (onChange) { - const modelFields = { - content, - postID, - Post: value, - post: post1, - }; - const result = onChange(modelFields); - value = result?.Post ?? value; - } - setPost(value); - setCurrentPostValue(undefined); - setCurrentPostDisplayValue(\\"\\"); - }} - currentFieldValue={currentPostValue} - label={\\"Post\\"} - items={Post ? [Post] : []} - hasError={errors?.Post?.hasError} - runValidationTasks={async () => - await runValidationTasks(\\"Post\\", currentPostValue) - } - errorMessage={errors?.Post?.errorMessage} - getBadgeText={getDisplayValue.Post} - setFieldValue={(model) => { - setCurrentPostDisplayValue(model ? getDisplayValue.Post(model) : \\"\\"); - setCurrentPostValue(model); - }} - inputFieldRef={PostRef} - defaultFieldValue={\\"\\"} - > - !PostIdSet.has(getIDValue.Post?.(r)) - ).map((r) => ({ - id: getIDValue.Post?.(r), - label: getDisplayValue.Post?.(r), - }))} - isLoading={PostLoading} - onSelect={({ id, label }) => { - setCurrentPostValue( - PostRecords.find((r) => - Object.entries(JSON.parse(id)).every( - ([key, value]) => r[key] === value - ) - ) - ); - setCurrentPostDisplayValue(label); - runValidationTasks(\\"Post\\", label); - }} - onClear={() => { - setCurrentPostDisplayValue(\\"\\"); - }} - defaultValue={Post} - onChange={(e) => { - let { value } = e.target; - fetchPostRecords(value); - if (errors.Post?.hasError) { - runValidationTasks(\\"Post\\", value); - } - setCurrentPostDisplayValue(value); - setCurrentPostValue(undefined); - }} - onBlur={() => runValidationTasks(\\"Post\\", currentPostDisplayValue)} - errorMessage={errors.Post?.errorMessage} - hasError={errors.Post?.hasError} - ref={PostRef} - labelHidden={true} - {...getOverrideProps(overrides, \\"Post\\")} - > - - { - let { value } = e.target; - if (onChange) { - const modelFields = { - content, - postID, - Post, - post: value, - }; - const result = onChange(modelFields); - value = result?.post ?? value; - } - if (errors.post?.hasError) { - runValidationTasks(\\"post\\", value); - } - setPost1(value); - }} - onBlur={() => runValidationTasks(\\"post\\", post1)} - errorMessage={errors.post?.errorMessage} - hasError={errors.post?.hasError} - {...getOverrideProps(overrides, \\"post\\")} - > = (value: T, validationResponse: Valid export declare type CommentUpdateFormInputValues = { content?: string; postID?: string; - Post?: any; - post?: string; }; export declare type CommentUpdateFormValidationValues = { content?: ValidationFunction; postID?: ValidationFunction; - Post?: ValidationFunction; - post?: ValidationFunction; }; export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes; export declare type CommentUpdateFormOverridesProps = { CommentUpdateFormGrid?: PrimitiveOverrideProps; content?: PrimitiveOverrideProps; postID?: PrimitiveOverrideProps; - Post?: PrimitiveOverrideProps; - post?: PrimitiveOverrideProps; } & EscapeHatchProps; export declare type CommentUpdateFormProps = React.PropsWithChildren<{ overrides?: CommentUpdateFormOverridesProps | undefined | null; } & { id?: string; - comment?: any; + comment?: Comment; onSubmit?: (fields: CommentUpdateFormInputValues) => CommentUpdateFormInputValues; onSuccess?: (fields: CommentUpdateFormInputValues) => void; onError?: (fields: CommentUpdateFormInputValues, errorMessage: string) => void; @@ -12594,7 +14657,7 @@ export default function CommentUpdateForm(props: CommentUpdateFormProps): React. " `; -exports[`amplify form renderer tests GraphQL form tests should generate an update form with hasMany relationship without types file 1`] = ` +exports[`amplify form renderer tests GraphQL form tests should generate an update form with many to many relationship - amplify js v6 1`] = ` "/* eslint-disable */ import * as React from \\"react\\"; import { @@ -12607,14 +14670,23 @@ import { Icon, ScrollView, Text, - TextField, useTheme, } from \\"@aws-amplify/ui-react\\"; import { getOverrideProps } from \\"@aws-amplify/ui-react/internal\\"; import { fetchByPath, validateField } from \\"./utils\\"; -import { API } from \\"aws-amplify\\"; -import { getComment, getPost, listPosts } from \\"../graphql/queries\\"; -import { updateComment } from \\"../graphql/mutations\\"; +import { + getClass, + listStudentClasses, + listStudents, + studentClassesByClassId, +} from \\"../graphql/queries\\"; +import { generateClient } from \\"aws-amplify/api\\"; +import { + createStudentClass, + deleteStudentClass, + updateClass, +} from \\"../graphql/mutations\\"; +const client = generateClient(); function ArrayField({ items = [], onChange, @@ -12770,10 +14842,10 @@ function ArrayField({ ); } -export default function CommentUpdateForm(props) { +export default function ClassUpdateForm(props) { const { id: idProp, - comment: commentModelProp, + class: classModelProp, onSuccess, onError, onSubmit, @@ -12783,90 +14855,69 @@ export default function CommentUpdateForm(props) { ...rest } = props; const initialValues = { - content: \\"\\", - postID: undefined, - Post: undefined, - post: \\"\\", + students: [], }; - const [content, setContent] = React.useState(initialValues.content); - const [postID, setPostID] = React.useState(initialValues.postID); - const [postIDLoading, setPostIDLoading] = React.useState(false); - const [postIDRecords, setPostIDRecords] = React.useState([]); - const [selectedPostIDRecords, setSelectedPostIDRecords] = React.useState([]); - const [Post, setPost] = React.useState(initialValues.Post); - const [PostLoading, setPostLoading] = React.useState(false); - const [PostRecords, setPostRecords] = React.useState([]); - const [post1, setPost1] = React.useState(initialValues.post); + const [students, setStudents] = React.useState(initialValues.students); + const [studentsLoading, setStudentsLoading] = React.useState(false); + const [studentsRecords, setStudentsRecords] = React.useState([]); const autocompleteLength = 10; const [errors, setErrors] = React.useState({}); const resetStateValues = () => { - const cleanValues = commentRecord - ? { ...initialValues, ...commentRecord, postID, Post } + const cleanValues = classRecord + ? { ...initialValues, ...classRecord, students: linkedStudents } : initialValues; - setContent(cleanValues.content); - setPostID(cleanValues.postID); - setCurrentPostIDValue(undefined); - setCurrentPostIDDisplayValue(\\"\\"); - setPost(cleanValues.Post); - setCurrentPostValue(undefined); - setCurrentPostDisplayValue(\\"\\"); - setPost1(cleanValues.post); + setStudents(cleanValues.students ?? []); + setCurrentStudentsValue(undefined); + setCurrentStudentsDisplayValue(\\"\\"); setErrors({}); }; - const [commentRecord, setCommentRecord] = React.useState(commentModelProp); + const [classRecord, setClassRecord] = React.useState(classModelProp); + const [linkedStudents, setLinkedStudents] = React.useState([]); + const canUnlinkStudents = false; React.useEffect(() => { const queryData = async () => { const record = idProp ? ( - await API.graphql({ - query: getComment.replaceAll(\\"__typename\\", \\"\\"), + await client.graphql({ + query: getClass.replaceAll(\\"__typename\\", \\"\\"), variables: { id: idProp }, }) - )?.data?.getComment - : commentModelProp; - const postIDRecord = record ? record.postID : undefined; - const postRecord = postIDRecord + )?.data?.getClass + : classModelProp; + const linkedStudents = record ? ( - await API.graphql({ - query: getPost.replaceAll(\\"__typename\\", \\"\\"), - variables: { id: postIDRecord }, + await client.graphql({ + query: studentClassesByClassId.replaceAll(\\"__typename\\", \\"\\"), + variables: { + classId: record.id, + }, }) - )?.data?.getPost - : undefined; - setPostID(postIDRecord); - setSelectedPostIDRecords([postRecord]); - const PostRecord = record ? await record.Post : undefined; - setPost(PostRecord); - setCommentRecord(record); + ).data.studentClassesByClassId.items.map((t) => t.student) + : []; + setLinkedStudents(linkedStudents); + setClassRecord(record); }; queryData(); - }, [idProp, commentModelProp]); - React.useEffect(resetStateValues, [commentRecord, postID, Post]); - const [currentPostIDDisplayValue, setCurrentPostIDDisplayValue] = - React.useState(\\"\\"); - const [currentPostIDValue, setCurrentPostIDValue] = React.useState(undefined); - const postIDRef = React.createRef(); - const [currentPostDisplayValue, setCurrentPostDisplayValue] = + }, [idProp, classModelProp]); + React.useEffect(resetStateValues, [classRecord, linkedStudents]); + const [currentStudentsDisplayValue, setCurrentStudentsDisplayValue] = React.useState(\\"\\"); - const [currentPostValue, setCurrentPostValue] = React.useState(undefined); - const PostRef = React.createRef(); + const [currentStudentsValue, setCurrentStudentsValue] = + React.useState(undefined); + const studentsRef = React.createRef(); const getIDValue = { - Post: (r) => JSON.stringify({ id: r?.id }), + students: (r) => JSON.stringify({ id: r?.id }), }; - const PostIdSet = new Set( - Array.isArray(Post) - ? Post.map((r) => getIDValue.Post?.(r)) - : getIDValue.Post?.(Post) + const studentsIdSet = new Set( + Array.isArray(students) + ? students.map((r) => getIDValue.students?.(r)) + : getIDValue.students?.(students) ); const getDisplayValue = { - postID: (r) => \`\${r?.title ? r?.title + \\" - \\" : \\"\\"}\${r?.id}\`, - Post: (r) => \`\${r?.title ? r?.title + \\" - \\" : \\"\\"}\${r?.id}\`, + students: (r) => r?.id, }; const validations = { - content: [], - postID: [{ type: \\"Required\\" }], - Post: [], - post: [], + students: [], }; const runValidationTasks = async ( fieldName, @@ -12885,65 +14936,35 @@ export default function CommentUpdateForm(props) { setErrors((errors) => ({ ...errors, [fieldName]: validationResponse })); return validationResponse; }; - const fetchPostIDRecords = async (value) => { - setPostIDLoading(true); - const newOptions = []; - let newNext = \\"\\"; - while (newOptions.length < autocompleteLength && newNext != null) { - const variables = { - limit: autocompleteLength * 5, - filter: { - or: [{ title: { contains: value } }, { id: { contains: value } }], - }, - }; - if (newNext) { - variables[\\"nextToken\\"] = newNext; - } - const result = ( - await API.graphql({ - query: listPosts.replaceAll(\\"__typename\\", \\"\\"), - variables, - }) - )?.data?.listPosts?.items; - var loaded = result.filter((item) => postID !== item.id); - newOptions.push(...loaded); - newNext = result.nextToken; - } - setPostIDRecords(newOptions.slice(0, autocompleteLength)); - setPostIDLoading(false); - }; - const fetchPostRecords = async (value) => { - setPostLoading(true); + const fetchStudentsRecords = async (value) => { + setStudentsLoading(true); const newOptions = []; let newNext = \\"\\"; while (newOptions.length < autocompleteLength && newNext != null) { const variables = { limit: autocompleteLength * 5, - filter: { - or: [{ title: { contains: value } }, { id: { contains: value } }], - }, + filter: { or: [{ id: { contains: value } }] }, }; if (newNext) { variables[\\"nextToken\\"] = newNext; } const result = ( - await API.graphql({ - query: listPosts.replaceAll(\\"__typename\\", \\"\\"), + await client.graphql({ + query: listStudents.replaceAll(\\"__typename\\", \\"\\"), variables, }) - )?.data?.listPosts?.items; + )?.data?.listStudents?.items; var loaded = result.filter( - (item) => !PostIdSet.has(getIDValue.Post?.(item)) + (item) => !studentsIdSet.has(getIDValue.students?.(item)) ); newOptions.push(...loaded); newNext = result.nextToken; } - setPostRecords(newOptions.slice(0, autocompleteLength)); - setPostLoading(false); + setStudentsRecords(newOptions.slice(0, autocompleteLength)); + setStudentsLoading(false); }; React.useEffect(() => { - fetchPostIDRecords(\\"\\"); - fetchPostRecords(\\"\\"); + fetchStudentsRecords(\\"\\"); }, []); return ( { event.preventDefault(); let modelFields = { - content: content ?? null, - postID, - Post: Post ?? null, - post: post ?? null, + students: students ?? null, }; const validationResponses = await Promise.all( Object.keys(validations).reduce((promises, fieldName) => { @@ -12995,20 +15013,104 @@ export default function CommentUpdateForm(props) { modelFields[key] = null; } }); - const modelFieldsToSave = { - content: modelFields.content ?? null, - postID: modelFields.postID, - postCommentsId: modelFields?.Post?.id ?? null, - }; - await API.graphql({ - query: updateComment.replaceAll(\\"__typename\\", \\"\\"), - variables: { - input: { - id: commentRecord.id, - ...modelFieldsToSave, - }, - }, + const promises = []; + const studentsToLinkMap = new Map(); + const studentsToUnLinkMap = new Map(); + const studentsMap = new Map(); + const linkedStudentsMap = new Map(); + students.forEach((r) => { + const count = studentsMap.get(getIDValue.students?.(r)); + const newCount = count ? count + 1 : 1; + studentsMap.set(getIDValue.students?.(r), newCount); + }); + linkedStudents.forEach((r) => { + const count = linkedStudentsMap.get(getIDValue.students?.(r)); + const newCount = count ? count + 1 : 1; + linkedStudentsMap.set(getIDValue.students?.(r), newCount); + }); + linkedStudentsMap.forEach((count, id) => { + const newCount = studentsMap.get(id); + if (newCount) { + const diffCount = count - newCount; + if (diffCount > 0) { + studentsToUnLinkMap.set(id, diffCount); + } + } else { + studentsToUnLinkMap.set(id, count); + } + }); + studentsMap.forEach((count, id) => { + const originalCount = linkedStudentsMap.get(id); + if (originalCount) { + const diffCount = count - originalCount; + if (diffCount > 0) { + studentsToLinkMap.set(id, diffCount); + } + } else { + studentsToLinkMap.set(id, count); + } + }); + studentsToUnLinkMap.forEach(async (count, id) => { + const recordKeys = JSON.parse(id); + const studentClassRecords = ( + await client.graphql({ + query: listStudentClasses.replaceAll(\\"__typename\\", \\"\\"), + variables: { + filter: { + and: [ + { studentId: { eq: recordKeys.id } }, + { classId: { eq: classRecord.id } }, + ], + }, + }, + }) + )?.data?.listStudentClasses?.items; + for (let i = 0; i < count; i++) { + promises.push( + client.graphql({ + query: deleteStudentClass.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + id: studentClassRecords[i].id, + }, + }, + }) + ); + } + }); + studentsToLinkMap.forEach((count, id) => { + const studentToLink = studentRecords.find((r) => + Object.entries(JSON.parse(id)).every( + ([key, value]) => r[key] === value + ) + ); + for (let i = count; i > 0; i--) { + promises.push( + client.graphql({ + query: createStudentClass.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + classId: classRecord.id, + studentId: studentToLink.id, + }, + }, + }) + ); + } }); + const modelFieldsToSave = {}; + promises.push( + client.graphql({ + query: updateClass.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + id: classRecord.id, + ...modelFieldsToSave, + }, + }, + }) + ); + await Promise.all(promises); if (onSuccess) { onSuccess(modelFields); } @@ -13019,233 +15121,85 @@ export default function CommentUpdateForm(props) { } } }} - {...getOverrideProps(overrides, \\"CommentUpdateForm\\")} + {...getOverrideProps(overrides, \\"ClassUpdateForm\\")} {...rest} > - { - let { value } = e.target; - if (onChange) { - const modelFields = { - content: value, - postID, - Post, - post: post1, - }; - const result = onChange(modelFields); - value = result?.content ?? value; - } - if (errors.content?.hasError) { - runValidationTasks(\\"content\\", value); - } - setContent(value); - }} - onBlur={() => runValidationTasks(\\"content\\", content)} - errorMessage={errors.content?.errorMessage} - hasError={errors.content?.hasError} - {...getOverrideProps(overrides, \\"content\\")} - > - { - let value = items[0]; - if (onChange) { - const modelFields = { - content, - postID: value, - Post, - post: post1, - }; - const result = onChange(modelFields); - value = result?.postID ?? value; - } - setPostID(value); - setCurrentPostIDValue(undefined); - }} - currentFieldValue={currentPostIDValue} - label={\\"Post id\\"} - items={postID ? [postID] : []} - hasError={errors?.postID?.hasError} - runValidationTasks={async () => - await runValidationTasks(\\"postID\\", currentPostIDValue) - } - errorMessage={errors?.postID?.errorMessage} - getBadgeText={(value) => - value - ? getDisplayValue.postID( - postIDRecords.find((r) => r.id === value) ?? - selectedPostIDRecords.find((r) => r.id === value) - ) - : \\"\\" - } - setFieldValue={(value) => { - setCurrentPostIDDisplayValue( - value - ? getDisplayValue.postID( - postIDRecords.find((r) => r.id === value) ?? - selectedPostIDRecords.find((r) => r.id === value) - ) - : \\"\\" - ); - setCurrentPostIDValue(value); - const selectedRecord = postIDRecords.find((r) => r.id === value); - if (selectedRecord) { - setSelectedPostIDRecords([selectedRecord]); - } - }} - inputFieldRef={postIDRef} - defaultFieldValue={\\"\\"} - > - - arr.findIndex((member) => member?.id === r?.id) === i - ) - .map((r) => ({ - id: r?.id, - label: getDisplayValue.postID?.(r), - }))} - isLoading={postIDLoading} - onSelect={({ id, label }) => { - setCurrentPostIDValue(id); - setCurrentPostIDDisplayValue(label); - runValidationTasks(\\"postID\\", label); - }} - onClear={() => { - setCurrentPostIDDisplayValue(\\"\\"); - }} - defaultValue={postID} - onChange={(e) => { - let { value } = e.target; - fetchPostIDRecords(value); - if (errors.postID?.hasError) { - runValidationTasks(\\"postID\\", value); - } - setCurrentPostIDDisplayValue(value); - setCurrentPostIDValue(undefined); - }} - onBlur={() => runValidationTasks(\\"postID\\", currentPostIDValue)} - errorMessage={errors.postID?.errorMessage} - hasError={errors.postID?.hasError} - ref={postIDRef} - labelHidden={true} - {...getOverrideProps(overrides, \\"postID\\")} - > - { - let value = items[0]; + let values = items; if (onChange) { const modelFields = { - content, - postID, - Post: value, - post: post1, + students: values, }; const result = onChange(modelFields); - value = result?.Post ?? value; + values = result?.students ?? values; } - setPost(value); - setCurrentPostValue(undefined); - setCurrentPostDisplayValue(\\"\\"); + setStudents(values); + setCurrentStudentsValue(undefined); + setCurrentStudentsDisplayValue(\\"\\"); }} - currentFieldValue={currentPostValue} - label={\\"Post\\"} - items={Post ? [Post] : []} - hasError={errors?.Post?.hasError} + currentFieldValue={currentStudentsValue} + label={\\"Students\\"} + items={students} + hasError={errors?.students?.hasError} runValidationTasks={async () => - await runValidationTasks(\\"Post\\", currentPostValue) + await runValidationTasks(\\"students\\", currentStudentsValue) } - errorMessage={errors?.Post?.errorMessage} - getBadgeText={getDisplayValue.Post} + errorMessage={errors?.students?.errorMessage} + getBadgeText={getDisplayValue.students} setFieldValue={(model) => { - setCurrentPostDisplayValue(model ? getDisplayValue.Post(model) : \\"\\"); - setCurrentPostValue(model); + setCurrentStudentsDisplayValue( + model ? getDisplayValue.students(model) : \\"\\" + ); + setCurrentStudentsValue(model); }} - inputFieldRef={PostRef} + inputFieldRef={studentsRef} defaultFieldValue={\\"\\"} > !PostIdSet.has(getIDValue.Post?.(r)) - ).map((r) => ({ - id: getIDValue.Post?.(r), - label: getDisplayValue.Post?.(r), + placeholder=\\"Search Student\\" + value={currentStudentsDisplayValue} + options={studentsRecords.map((r) => ({ + id: getIDValue.students?.(r), + label: getDisplayValue.students?.(r), }))} - isLoading={PostLoading} + isLoading={studentsLoading} onSelect={({ id, label }) => { - setCurrentPostValue( - PostRecords.find((r) => + setCurrentStudentsValue( + studentsRecords.find((r) => Object.entries(JSON.parse(id)).every( ([key, value]) => r[key] === value ) ) ); - setCurrentPostDisplayValue(label); - runValidationTasks(\\"Post\\", label); + setCurrentStudentsDisplayValue(label); + runValidationTasks(\\"students\\", label); }} onClear={() => { - setCurrentPostDisplayValue(\\"\\"); + setCurrentStudentsDisplayValue(\\"\\"); }} - defaultValue={Post} onChange={(e) => { let { value } = e.target; - fetchPostRecords(value); - if (errors.Post?.hasError) { - runValidationTasks(\\"Post\\", value); + fetchStudentsRecords(value); + if (errors.students?.hasError) { + runValidationTasks(\\"students\\", value); } - setCurrentPostDisplayValue(value); - setCurrentPostValue(undefined); + setCurrentStudentsDisplayValue(value); + setCurrentStudentsValue(undefined); }} - onBlur={() => runValidationTasks(\\"Post\\", currentPostDisplayValue)} - errorMessage={errors.Post?.errorMessage} - hasError={errors.Post?.hasError} - ref={PostRef} + onBlur={() => + runValidationTasks(\\"students\\", currentStudentsDisplayValue) + } + errorMessage={errors.students?.errorMessage} + hasError={errors.students?.hasError} + ref={studentsRef} labelHidden={true} - {...getOverrideProps(overrides, \\"Post\\")} + {...getOverrideProps(overrides, \\"students\\")} > - { - let { value } = e.target; - if (onChange) { - const modelFields = { - content, - postID, - Post, - post: value, - }; - const result = onChange(modelFields); - value = result?.post ?? value; - } - if (errors.post?.hasError) { - runValidationTasks(\\"post\\", value); - } - setPost1(value); - }} - onBlur={() => runValidationTasks(\\"post\\", post1)} - errorMessage={errors.post?.errorMessage} - hasError={errors.post?.hasError} - {...getOverrideProps(overrides, \\"post\\")} - > e?.hasError) } {...getOverrideProps(overrides, \\"SubmitButton\\")} @@ -13282,51 +15236,43 @@ export default function CommentUpdateForm(props) { " `; -exports[`amplify form renderer tests GraphQL form tests should generate an update form with hasMany relationship without types file 2`] = ` +exports[`amplify form renderer tests GraphQL form tests should generate an update form with many to many relationship - amplify js v6 2`] = ` "import * as React from \\"react\\"; -import { AutocompleteProps, GridProps, TextFieldProps } from \\"@aws-amplify/ui-react\\"; +import { AutocompleteProps, GridProps } from \\"@aws-amplify/ui-react\\"; import { EscapeHatchProps } from \\"@aws-amplify/ui-react/internal\\"; +import { Class, Student } from \\"../API\\"; export declare type ValidationResponse = { hasError: boolean; errorMessage?: string; }; export declare type ValidationFunction = (value: T, validationResponse: ValidationResponse) => ValidationResponse | Promise; -export declare type CommentUpdateFormInputValues = { - content?: string; - postID?: string; - Post?: any; - post?: string; +export declare type ClassUpdateFormInputValues = { + students?: Student[]; }; -export declare type CommentUpdateFormValidationValues = { - content?: ValidationFunction; - postID?: ValidationFunction; - Post?: ValidationFunction; - post?: ValidationFunction; +export declare type ClassUpdateFormValidationValues = { + students?: ValidationFunction; }; export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes; -export declare type CommentUpdateFormOverridesProps = { - CommentUpdateFormGrid?: PrimitiveOverrideProps; - content?: PrimitiveOverrideProps; - postID?: PrimitiveOverrideProps; - Post?: PrimitiveOverrideProps; - post?: PrimitiveOverrideProps; +export declare type ClassUpdateFormOverridesProps = { + ClassUpdateFormGrid?: PrimitiveOverrideProps; + students?: PrimitiveOverrideProps; } & EscapeHatchProps; -export declare type CommentUpdateFormProps = React.PropsWithChildren<{ - overrides?: CommentUpdateFormOverridesProps | undefined | null; +export declare type ClassUpdateFormProps = React.PropsWithChildren<{ + overrides?: ClassUpdateFormOverridesProps | undefined | null; } & { id?: string; - comment?: any; - onSubmit?: (fields: CommentUpdateFormInputValues) => CommentUpdateFormInputValues; - onSuccess?: (fields: CommentUpdateFormInputValues) => void; - onError?: (fields: CommentUpdateFormInputValues, errorMessage: string) => void; - onChange?: (fields: CommentUpdateFormInputValues) => CommentUpdateFormInputValues; - onValidate?: CommentUpdateFormValidationValues; + class?: Class; + onSubmit?: (fields: ClassUpdateFormInputValues) => ClassUpdateFormInputValues; + onSuccess?: (fields: ClassUpdateFormInputValues) => void; + onError?: (fields: ClassUpdateFormInputValues, errorMessage: string) => void; + onChange?: (fields: ClassUpdateFormInputValues) => ClassUpdateFormInputValues; + onValidate?: ClassUpdateFormValidationValues; } & React.CSSProperties>; -export default function CommentUpdateForm(props: CommentUpdateFormProps): React.ReactElement; +export default function ClassUpdateForm(props: ClassUpdateFormProps): React.ReactElement; " `; -exports[`amplify form renderer tests GraphQL form tests should generate an update form with id field instead of belongsTo 1`] = ` +exports[`amplify form renderer tests GraphQL form tests should generate an update form with many to many relationship 1`] = ` "/* eslint-disable */ import * as React from \\"react\\"; import { @@ -13339,14 +15285,22 @@ import { Icon, ScrollView, Text, - TextField, useTheme, } from \\"@aws-amplify/ui-react\\"; import { getOverrideProps } from \\"@aws-amplify/ui-react/internal\\"; import { fetchByPath, validateField } from \\"./utils\\"; +import { + getClass, + listStudentClasses, + listStudents, + studentClassesByClassId, +} from \\"../graphql/queries\\"; import { API } from \\"aws-amplify\\"; -import { getComment, getPost, listPosts } from \\"../graphql/queries\\"; -import { updateComment } from \\"../graphql/mutations\\"; +import { + createStudentClass, + deleteStudentClass, + updateClass, +} from \\"../graphql/mutations\\"; function ArrayField({ items = [], onChange, @@ -13502,10 +15456,10 @@ function ArrayField({ ); } -export default function CommentUpdateForm(props) { +export default function ClassUpdateForm(props) { const { id: idProp, - comment: commentModelProp, + class: classModelProp, onSuccess, onError, onSubmit, @@ -13515,63 +15469,69 @@ export default function CommentUpdateForm(props) { ...rest } = props; const initialValues = { - content: \\"\\", - postID: undefined, + students: [], }; - const [content, setContent] = React.useState(initialValues.content); - const [postID, setPostID] = React.useState(initialValues.postID); - const [postIDLoading, setPostIDLoading] = React.useState(false); - const [postIDRecords, setPostIDRecords] = React.useState([]); - const [selectedPostIDRecords, setSelectedPostIDRecords] = React.useState([]); + const [students, setStudents] = React.useState(initialValues.students); + const [studentsLoading, setStudentsLoading] = React.useState(false); + const [studentsRecords, setStudentsRecords] = React.useState([]); const autocompleteLength = 10; const [errors, setErrors] = React.useState({}); const resetStateValues = () => { - const cleanValues = commentRecord - ? { ...initialValues, ...commentRecord, postID } + const cleanValues = classRecord + ? { ...initialValues, ...classRecord, students: linkedStudents } : initialValues; - setContent(cleanValues.content); - setPostID(cleanValues.postID); - setCurrentPostIDValue(undefined); - setCurrentPostIDDisplayValue(\\"\\"); + setStudents(cleanValues.students ?? []); + setCurrentStudentsValue(undefined); + setCurrentStudentsDisplayValue(\\"\\"); setErrors({}); }; - const [commentRecord, setCommentRecord] = React.useState(commentModelProp); + const [classRecord, setClassRecord] = React.useState(classModelProp); + const [linkedStudents, setLinkedStudents] = React.useState([]); + const canUnlinkStudents = false; React.useEffect(() => { const queryData = async () => { const record = idProp ? ( await API.graphql({ - query: getComment.replaceAll(\\"__typename\\", \\"\\"), + query: getClass.replaceAll(\\"__typename\\", \\"\\"), variables: { id: idProp }, }) - )?.data?.getComment - : commentModelProp; - const postIDRecord = record ? record.postID : undefined; - const postRecord = postIDRecord + )?.data?.getClass + : classModelProp; + const linkedStudents = record ? ( await API.graphql({ - query: getPost.replaceAll(\\"__typename\\", \\"\\"), - variables: { id: postIDRecord }, + query: studentClassesByClassId.replaceAll(\\"__typename\\", \\"\\"), + variables: { + classId: record.id, + }, }) - )?.data?.getPost - : undefined; - setPostID(postIDRecord); - setSelectedPostIDRecords([postRecord]); - setCommentRecord(record); + ).data.studentClassesByClassId.items.map((t) => t.student) + : []; + setLinkedStudents(linkedStudents); + setClassRecord(record); }; queryData(); - }, [idProp, commentModelProp]); - React.useEffect(resetStateValues, [commentRecord, postID]); - const [currentPostIDDisplayValue, setCurrentPostIDDisplayValue] = + }, [idProp, classModelProp]); + React.useEffect(resetStateValues, [classRecord, linkedStudents]); + const [currentStudentsDisplayValue, setCurrentStudentsDisplayValue] = React.useState(\\"\\"); - const [currentPostIDValue, setCurrentPostIDValue] = React.useState(undefined); - const postIDRef = React.createRef(); + const [currentStudentsValue, setCurrentStudentsValue] = + React.useState(undefined); + const studentsRef = React.createRef(); + const getIDValue = { + students: (r) => JSON.stringify({ id: r?.id }), + }; + const studentsIdSet = new Set( + Array.isArray(students) + ? students.map((r) => getIDValue.students?.(r)) + : getIDValue.students?.(students) + ); const getDisplayValue = { - postID: (r) => \`\${r?.title ? r?.title + \\" - \\" : \\"\\"}\${r?.id}\`, + students: (r) => r?.id, }; const validations = { - content: [{ type: \\"Required\\" }], - postID: [], + students: [], }; const runValidationTasks = async ( fieldName, @@ -13590,35 +15550,35 @@ export default function CommentUpdateForm(props) { setErrors((errors) => ({ ...errors, [fieldName]: validationResponse })); return validationResponse; }; - const fetchPostIDRecords = async (value) => { - setPostIDLoading(true); + const fetchStudentsRecords = async (value) => { + setStudentsLoading(true); const newOptions = []; let newNext = \\"\\"; while (newOptions.length < autocompleteLength && newNext != null) { const variables = { limit: autocompleteLength * 5, - filter: { - or: [{ title: { contains: value } }, { id: { contains: value } }], - }, + filter: { or: [{ id: { contains: value } }] }, }; if (newNext) { variables[\\"nextToken\\"] = newNext; } const result = ( await API.graphql({ - query: listPosts.replaceAll(\\"__typename\\", \\"\\"), + query: listStudents.replaceAll(\\"__typename\\", \\"\\"), variables, }) - )?.data?.listPosts?.items; - var loaded = result.filter((item) => postID !== item.id); + )?.data?.listStudents?.items; + var loaded = result.filter( + (item) => !studentsIdSet.has(getIDValue.students?.(item)) + ); newOptions.push(...loaded); newNext = result.nextToken; } - setPostIDRecords(newOptions.slice(0, autocompleteLength)); - setPostIDLoading(false); + setStudentsRecords(newOptions.slice(0, autocompleteLength)); + setStudentsLoading(false); }; React.useEffect(() => { - fetchPostIDRecords(\\"\\"); + fetchStudentsRecords(\\"\\"); }, []); return ( { event.preventDefault(); let modelFields = { - content, - postID: postID ?? null, + students: students ?? null, }; const validationResponses = await Promise.all( Object.keys(validations).reduce((promises, fieldName) => { if (Array.isArray(modelFields[fieldName])) { promises.push( ...modelFields[fieldName].map((item) => - runValidationTasks(fieldName, item) + runValidationTasks( + fieldName, + item, + getDisplayValue[fieldName] + ) ) ); - return promises; + return promises; + } + promises.push( + runValidationTasks( + fieldName, + modelFields[fieldName], + getDisplayValue[fieldName] + ) + ); + return promises; + }, []) + ); + if (validationResponses.some((r) => r.hasError)) { + return; + } + if (onSubmit) { + modelFields = onSubmit(modelFields); + } + try { + Object.entries(modelFields).forEach(([key, value]) => { + if (typeof value === \\"string\\" && value === \\"\\") { + modelFields[key] = null; + } + }); + const promises = []; + const studentsToLinkMap = new Map(); + const studentsToUnLinkMap = new Map(); + const studentsMap = new Map(); + const linkedStudentsMap = new Map(); + students.forEach((r) => { + const count = studentsMap.get(getIDValue.students?.(r)); + const newCount = count ? count + 1 : 1; + studentsMap.set(getIDValue.students?.(r), newCount); + }); + linkedStudents.forEach((r) => { + const count = linkedStudentsMap.get(getIDValue.students?.(r)); + const newCount = count ? count + 1 : 1; + linkedStudentsMap.set(getIDValue.students?.(r), newCount); + }); + linkedStudentsMap.forEach((count, id) => { + const newCount = studentsMap.get(id); + if (newCount) { + const diffCount = count - newCount; + if (diffCount > 0) { + studentsToUnLinkMap.set(id, diffCount); + } + } else { + studentsToUnLinkMap.set(id, count); + } + }); + studentsMap.forEach((count, id) => { + const originalCount = linkedStudentsMap.get(id); + if (originalCount) { + const diffCount = count - originalCount; + if (diffCount > 0) { + studentsToLinkMap.set(id, diffCount); + } + } else { + studentsToLinkMap.set(id, count); + } + }); + studentsToUnLinkMap.forEach(async (count, id) => { + const recordKeys = JSON.parse(id); + const studentClassRecords = ( + await API.graphql({ + query: listStudentClasses.replaceAll(\\"__typename\\", \\"\\"), + variables: { + filter: { + and: [ + { studentId: { eq: recordKeys.id } }, + { classId: { eq: classRecord.id } }, + ], + }, + }, + }) + )?.data?.listStudentClasses?.items; + for (let i = 0; i < count; i++) { + promises.push( + API.graphql({ + query: deleteStudentClass.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + id: studentClassRecords[i].id, + }, + }, + }) + ); } - promises.push( - runValidationTasks(fieldName, modelFields[fieldName]) + }); + studentsToLinkMap.forEach((count, id) => { + const studentToLink = studentRecords.find((r) => + Object.entries(JSON.parse(id)).every( + ([key, value]) => r[key] === value + ) ); - return promises; - }, []) - ); - if (validationResponses.some((r) => r.hasError)) { - return; - } - if (onSubmit) { - modelFields = onSubmit(modelFields); - } - try { - Object.entries(modelFields).forEach(([key, value]) => { - if (typeof value === \\"string\\" && value === \\"\\") { - modelFields[key] = null; + for (let i = count; i > 0; i--) { + promises.push( + API.graphql({ + query: createStudentClass.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + classId: classRecord.id, + studentId: studentToLink.id, + }, + }, + }) + ); } }); - await API.graphql({ - query: updateComment.replaceAll(\\"__typename\\", \\"\\"), - variables: { - input: { - id: commentRecord.id, - ...modelFields, + const modelFieldsToSave = {}; + promises.push( + API.graphql({ + query: updateClass.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + id: classRecord.id, + ...modelFieldsToSave, + }, }, - }, - }); + }) + ); + await Promise.all(promises); if (onSuccess) { onSuccess(modelFields); } @@ -13679,123 +15735,83 @@ export default function CommentUpdateForm(props) { } } }} - {...getOverrideProps(overrides, \\"CommentUpdateForm\\")} + {...getOverrideProps(overrides, \\"ClassUpdateForm\\")} {...rest} > - { - let { value } = e.target; - if (onChange) { - const modelFields = { - content: value, - postID, - }; - const result = onChange(modelFields); - value = result?.content ?? value; - } - if (errors.content?.hasError) { - runValidationTasks(\\"content\\", value); - } - setContent(value); - }} - onBlur={() => runValidationTasks(\\"content\\", content)} - errorMessage={errors.content?.errorMessage} - hasError={errors.content?.hasError} - {...getOverrideProps(overrides, \\"content\\")} - > { - let value = items[0]; + let values = items; if (onChange) { const modelFields = { - content, - postID: value, + students: values, }; const result = onChange(modelFields); - value = result?.postID ?? value; + values = result?.students ?? values; } - setPostID(value); - setCurrentPostIDValue(undefined); + setStudents(values); + setCurrentStudentsValue(undefined); + setCurrentStudentsDisplayValue(\\"\\"); }} - currentFieldValue={currentPostIDValue} - label={\\"Post id\\"} - items={postID ? [postID] : []} - hasError={errors?.postID?.hasError} + currentFieldValue={currentStudentsValue} + label={\\"Students\\"} + items={students} + hasError={errors?.students?.hasError} runValidationTasks={async () => - await runValidationTasks(\\"postID\\", currentPostIDValue) - } - errorMessage={errors?.postID?.errorMessage} - getBadgeText={(value) => - value - ? getDisplayValue.postID( - postIDRecords.find((r) => r.id === value) ?? - selectedPostIDRecords.find((r) => r.id === value) - ) - : \\"\\" + await runValidationTasks(\\"students\\", currentStudentsValue) } - setFieldValue={(value) => { - setCurrentPostIDDisplayValue( - value - ? getDisplayValue.postID( - postIDRecords.find((r) => r.id === value) ?? - selectedPostIDRecords.find((r) => r.id === value) - ) - : \\"\\" + errorMessage={errors?.students?.errorMessage} + getBadgeText={getDisplayValue.students} + setFieldValue={(model) => { + setCurrentStudentsDisplayValue( + model ? getDisplayValue.students(model) : \\"\\" ); - setCurrentPostIDValue(value); - const selectedRecord = postIDRecords.find((r) => r.id === value); - if (selectedRecord) { - setSelectedPostIDRecords([selectedRecord]); - } + setCurrentStudentsValue(model); }} - inputFieldRef={postIDRef} + inputFieldRef={studentsRef} defaultFieldValue={\\"\\"} > - arr.findIndex((member) => member?.id === r?.id) === i - ) - .map((r) => ({ - id: r?.id, - label: getDisplayValue.postID?.(r), - }))} - isLoading={postIDLoading} + placeholder=\\"Search Student\\" + value={currentStudentsDisplayValue} + options={studentsRecords.map((r) => ({ + id: getIDValue.students?.(r), + label: getDisplayValue.students?.(r), + }))} + isLoading={studentsLoading} onSelect={({ id, label }) => { - setCurrentPostIDValue(id); - setCurrentPostIDDisplayValue(label); - runValidationTasks(\\"postID\\", label); + setCurrentStudentsValue( + studentsRecords.find((r) => + Object.entries(JSON.parse(id)).every( + ([key, value]) => r[key] === value + ) + ) + ); + setCurrentStudentsDisplayValue(label); + runValidationTasks(\\"students\\", label); }} onClear={() => { - setCurrentPostIDDisplayValue(\\"\\"); + setCurrentStudentsDisplayValue(\\"\\"); }} - defaultValue={postID} onChange={(e) => { let { value } = e.target; - fetchPostIDRecords(value); - if (errors.postID?.hasError) { - runValidationTasks(\\"postID\\", value); + fetchStudentsRecords(value); + if (errors.students?.hasError) { + runValidationTasks(\\"students\\", value); } - setCurrentPostIDDisplayValue(value); - setCurrentPostIDValue(undefined); + setCurrentStudentsDisplayValue(value); + setCurrentStudentsValue(undefined); }} - onBlur={() => runValidationTasks(\\"postID\\", currentPostIDValue)} - errorMessage={errors.postID?.errorMessage} - hasError={errors.postID?.hasError} - ref={postIDRef} + onBlur={() => + runValidationTasks(\\"students\\", currentStudentsDisplayValue) + } + errorMessage={errors.students?.errorMessage} + hasError={errors.students?.hasError} + ref={studentsRef} labelHidden={true} - {...getOverrideProps(overrides, \\"postID\\")} + {...getOverrideProps(overrides, \\"students\\")} > e?.hasError) } {...getOverrideProps(overrides, \\"SubmitButton\\")} @@ -13834,46 +15850,43 @@ export default function CommentUpdateForm(props) { " `; -exports[`amplify form renderer tests GraphQL form tests should generate an update form with id field instead of belongsTo 2`] = ` +exports[`amplify form renderer tests GraphQL form tests should generate an update form with many to many relationship 2`] = ` "import * as React from \\"react\\"; -import { AutocompleteProps, GridProps, TextFieldProps } from \\"@aws-amplify/ui-react\\"; +import { AutocompleteProps, GridProps } from \\"@aws-amplify/ui-react\\"; import { EscapeHatchProps } from \\"@aws-amplify/ui-react/internal\\"; -import { Comment } from \\"../API\\"; +import { Class, Student } from \\"../API\\"; export declare type ValidationResponse = { hasError: boolean; errorMessage?: string; }; export declare type ValidationFunction = (value: T, validationResponse: ValidationResponse) => ValidationResponse | Promise; -export declare type CommentUpdateFormInputValues = { - content?: string; - postID?: string; +export declare type ClassUpdateFormInputValues = { + students?: Student[]; }; -export declare type CommentUpdateFormValidationValues = { - content?: ValidationFunction; - postID?: ValidationFunction; +export declare type ClassUpdateFormValidationValues = { + students?: ValidationFunction; }; export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes; -export declare type CommentUpdateFormOverridesProps = { - CommentUpdateFormGrid?: PrimitiveOverrideProps; - content?: PrimitiveOverrideProps; - postID?: PrimitiveOverrideProps; +export declare type ClassUpdateFormOverridesProps = { + ClassUpdateFormGrid?: PrimitiveOverrideProps; + students?: PrimitiveOverrideProps; } & EscapeHatchProps; -export declare type CommentUpdateFormProps = React.PropsWithChildren<{ - overrides?: CommentUpdateFormOverridesProps | undefined | null; +export declare type ClassUpdateFormProps = React.PropsWithChildren<{ + overrides?: ClassUpdateFormOverridesProps | undefined | null; } & { id?: string; - comment?: Comment; - onSubmit?: (fields: CommentUpdateFormInputValues) => CommentUpdateFormInputValues; - onSuccess?: (fields: CommentUpdateFormInputValues) => void; - onError?: (fields: CommentUpdateFormInputValues, errorMessage: string) => void; - onChange?: (fields: CommentUpdateFormInputValues) => CommentUpdateFormInputValues; - onValidate?: CommentUpdateFormValidationValues; + class?: Class; + onSubmit?: (fields: ClassUpdateFormInputValues) => ClassUpdateFormInputValues; + onSuccess?: (fields: ClassUpdateFormInputValues) => void; + onError?: (fields: ClassUpdateFormInputValues, errorMessage: string) => void; + onChange?: (fields: ClassUpdateFormInputValues) => ClassUpdateFormInputValues; + onValidate?: ClassUpdateFormValidationValues; } & React.CSSProperties>; -export default function CommentUpdateForm(props: CommentUpdateFormProps): React.ReactElement; +export default function ClassUpdateForm(props: ClassUpdateFormProps): React.ReactElement; " `; -exports[`amplify form renderer tests GraphQL form tests should generate an update form with many to many relationship 1`] = ` +exports[`amplify form renderer tests GraphQL form tests should generate an update form with nonModel field 1`] = ` "/* eslint-disable */ import * as React from \\"react\\"; import { @@ -13886,22 +15899,15 @@ import { Icon, ScrollView, Text, + TextAreaField, + TextField, useTheme, } from \\"@aws-amplify/ui-react\\"; import { getOverrideProps } from \\"@aws-amplify/ui-react/internal\\"; import { fetchByPath, validateField } from \\"./utils\\"; -import { - getClass, - listStudentClasses, - listStudents, - studentClassesByClassId, -} from \\"../graphql/queries\\"; import { API } from \\"aws-amplify\\"; -import { - createStudentClass, - deleteStudentClass, - updateClass, -} from \\"../graphql/mutations\\"; +import { getBasicTable, listParentTables } from \\"../graphql/queries\\"; +import { updateBasicTable } from \\"../graphql/mutations\\"; function ArrayField({ items = [], onChange, @@ -14057,10 +16063,10 @@ function ArrayField({ ); } -export default function ClassUpdateForm(props) { +export default function UpdateForm(props) { const { id: idProp, - class: classModelProp, + basicTable: basicTableModelProp, onSuccess, onError, onSubmit, @@ -14070,69 +16076,73 @@ export default function ClassUpdateForm(props) { ...rest } = props; const initialValues = { - students: [], + name: \\"\\", + nmTest: \\"\\", + parentTable: undefined, }; - const [students, setStudents] = React.useState(initialValues.students); - const [studentsLoading, setStudentsLoading] = React.useState(false); - const [studentsRecords, setStudentsRecords] = React.useState([]); + const [name, setName] = React.useState(initialValues.name); + const [nmTest, setNmTest] = React.useState(initialValues.nmTest); + const [parentTable, setParentTable] = React.useState( + initialValues.parentTable + ); + const [parentTableLoading, setParentTableLoading] = React.useState(false); + const [parentTableRecords, setParentTableRecords] = React.useState([]); const autocompleteLength = 10; const [errors, setErrors] = React.useState({}); const resetStateValues = () => { - const cleanValues = classRecord - ? { ...initialValues, ...classRecord, students: linkedStudents } + const cleanValues = basicTableRecord + ? { ...initialValues, ...basicTableRecord, parentTable } : initialValues; - setStudents(cleanValues.students ?? []); - setCurrentStudentsValue(undefined); - setCurrentStudentsDisplayValue(\\"\\"); + setName(cleanValues.name); + setNmTest( + typeof cleanValues.nmTest === \\"string\\" || cleanValues.nmTest === null + ? cleanValues.nmTest + : JSON.stringify(cleanValues.nmTest) + ); + setParentTable(cleanValues.parentTable); + setCurrentParentTableValue(undefined); + setCurrentParentTableDisplayValue(\\"\\"); setErrors({}); }; - const [classRecord, setClassRecord] = React.useState(classModelProp); - const [linkedStudents, setLinkedStudents] = React.useState([]); - const canUnlinkStudents = false; + const [basicTableRecord, setBasicTableRecord] = + React.useState(basicTableModelProp); React.useEffect(() => { const queryData = async () => { const record = idProp ? ( await API.graphql({ - query: getClass.replaceAll(\\"__typename\\", \\"\\"), + query: getBasicTable.replaceAll(\\"__typename\\", \\"\\"), variables: { id: idProp }, }) - )?.data?.getClass - : classModelProp; - const linkedStudents = record - ? ( - await API.graphql({ - query: studentClassesByClassId.replaceAll(\\"__typename\\", \\"\\"), - variables: { - classId: record.id, - }, - }) - ).data.studentClassesByClassId.items.map((t) => t.student) - : []; - setLinkedStudents(linkedStudents); - setClassRecord(record); + )?.data?.getBasicTable + : basicTableModelProp; + const parentTableRecord = record ? await record.parentTable : undefined; + setParentTable(parentTableRecord); + setBasicTableRecord(record); }; queryData(); - }, [idProp, classModelProp]); - React.useEffect(resetStateValues, [classRecord, linkedStudents]); - const [currentStudentsDisplayValue, setCurrentStudentsDisplayValue] = + }, [idProp, basicTableModelProp]); + React.useEffect(resetStateValues, [basicTableRecord, parentTable]); + const [currentParentTableDisplayValue, setCurrentParentTableDisplayValue] = React.useState(\\"\\"); - const [currentStudentsValue, setCurrentStudentsValue] = + const [currentParentTableValue, setCurrentParentTableValue] = React.useState(undefined); - const studentsRef = React.createRef(); + const parentTableRef = React.createRef(); const getIDValue = { - students: (r) => JSON.stringify({ id: r?.id }), + parentTable: (r) => JSON.stringify({ id: r?.id }), }; - const studentsIdSet = new Set( - Array.isArray(students) - ? students.map((r) => getIDValue.students?.(r)) - : getIDValue.students?.(students) + const parentTableIdSet = new Set( + Array.isArray(parentTable) + ? parentTable.map((r) => getIDValue.parentTable?.(r)) + : getIDValue.parentTable?.(parentTable) ); const getDisplayValue = { - students: (r) => r?.id, + parentTable: (r) => \`\${r?.name ? r?.name + \\" - \\" : \\"\\"}\${r?.id}\`, }; const validations = { - students: [], + name: [], + nmTest: [{ type: \\"JSON\\" }], + parentTable: [], }; const runValidationTasks = async ( fieldName, @@ -14151,35 +16161,37 @@ export default function ClassUpdateForm(props) { setErrors((errors) => ({ ...errors, [fieldName]: validationResponse })); return validationResponse; }; - const fetchStudentsRecords = async (value) => { - setStudentsLoading(true); + const fetchParentTableRecords = async (value) => { + setParentTableLoading(true); const newOptions = []; let newNext = \\"\\"; while (newOptions.length < autocompleteLength && newNext != null) { const variables = { limit: autocompleteLength * 5, - filter: { or: [{ id: { contains: value } }] }, + filter: { + or: [{ name: { contains: value } }, { id: { contains: value } }], + }, }; if (newNext) { variables[\\"nextToken\\"] = newNext; } const result = ( await API.graphql({ - query: listStudents.replaceAll(\\"__typename\\", \\"\\"), + query: listParentTables.replaceAll(\\"__typename\\", \\"\\"), variables, }) - )?.data?.listStudents?.items; + )?.data?.listParentTables?.items; var loaded = result.filter( - (item) => !studentsIdSet.has(getIDValue.students?.(item)) + (item) => !parentTableIdSet.has(getIDValue.parentTable?.(item)) ); newOptions.push(...loaded); newNext = result.nextToken; } - setStudentsRecords(newOptions.slice(0, autocompleteLength)); - setStudentsLoading(false); + setParentTableRecords(newOptions.slice(0, autocompleteLength)); + setParentTableLoading(false); }; React.useEffect(() => { - fetchStudentsRecords(\\"\\"); + fetchParentTableRecords(\\"\\"); }, []); return ( { event.preventDefault(); let modelFields = { - students: students ?? null, + name: name ?? null, + nmTest: nmTest ?? null, + parentTable: parentTable ?? null, }; const validationResponses = await Promise.all( Object.keys(validations).reduce((promises, fieldName) => { @@ -14228,191 +16242,167 @@ export default function ClassUpdateForm(props) { modelFields[key] = null; } }); - const promises = []; - const studentsToLinkMap = new Map(); - const studentsToUnLinkMap = new Map(); - const studentsMap = new Map(); - const linkedStudentsMap = new Map(); - students.forEach((r) => { - const count = studentsMap.get(getIDValue.students?.(r)); - const newCount = count ? count + 1 : 1; - studentsMap.set(getIDValue.students?.(r), newCount); - }); - linkedStudents.forEach((r) => { - const count = linkedStudentsMap.get(getIDValue.students?.(r)); - const newCount = count ? count + 1 : 1; - linkedStudentsMap.set(getIDValue.students?.(r), newCount); - }); - linkedStudentsMap.forEach((count, id) => { - const newCount = studentsMap.get(id); - if (newCount) { - const diffCount = count - newCount; - if (diffCount > 0) { - studentsToUnLinkMap.set(id, diffCount); - } - } else { - studentsToUnLinkMap.set(id, count); - } - }); - studentsMap.forEach((count, id) => { - const originalCount = linkedStudentsMap.get(id); - if (originalCount) { - const diffCount = count - originalCount; - if (diffCount > 0) { - studentsToLinkMap.set(id, diffCount); - } - } else { - studentsToLinkMap.set(id, count); - } - }); - studentsToUnLinkMap.forEach(async (count, id) => { - const recordKeys = JSON.parse(id); - const studentClassRecords = ( - await API.graphql({ - query: listStudentClasses.replaceAll(\\"__typename\\", \\"\\"), - variables: { - filter: { - and: [ - { studentId: { eq: recordKeys.id } }, - { classId: { eq: classRecord.id } }, - ], - }, - }, - }) - )?.data?.listStudentClasses?.items; - for (let i = 0; i < count; i++) { - promises.push( - API.graphql({ - query: deleteStudentClass.replaceAll(\\"__typename\\", \\"\\"), - variables: { - input: { - id: studentClassRecords[i].id, - }, - }, - }) - ); - } - }); - studentsToLinkMap.forEach((count, id) => { - const studentToLink = studentRecords.find((r) => - Object.entries(JSON.parse(id)).every( - ([key, value]) => r[key] === value - ) - ); - for (let i = count; i > 0; i--) { - promises.push( - API.graphql({ - query: createStudentClass.replaceAll(\\"__typename\\", \\"\\"), - variables: { - input: { - classId: classRecord.id, - studentId: studentToLink.id, - }, - }, - }) - ); - } - }); - const modelFieldsToSave = {}; - promises.push( - API.graphql({ - query: updateClass.replaceAll(\\"__typename\\", \\"\\"), - variables: { - input: { - id: classRecord.id, - ...modelFieldsToSave, - }, + const modelFieldsToSave = { + name: modelFields.name ?? null, + parentTableBasicTablesId: modelFields?.parentTable?.id ?? null, + nmTest: modelFields.nmTest + ? JSON.parse(modelFields.nmTest) + : modelFields.nmTest, + }; + await API.graphql({ + query: updateBasicTable.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + id: basicTableRecord.id, + ...modelFieldsToSave, }, - }) - ); - await Promise.all(promises); + }, + }); if (onSuccess) { onSuccess(modelFields); } - } catch (err) { - if (onError) { - const messages = err.errors.map((e) => e.message).join(\\"\\\\n\\"); - onError(modelFields, messages); + } catch (err) { + if (onError) { + const messages = err.errors.map((e) => e.message).join(\\"\\\\n\\"); + onError(modelFields, messages); + } + } + }} + {...getOverrideProps(overrides, \\"UpdateForm\\")} + {...rest} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + name: value, + nmTest, + parentTable, + }; + const result = onChange(modelFields); + value = result?.name ?? value; + } + if (errors.name?.hasError) { + runValidationTasks(\\"name\\", value); + } + setName(value); + }} + onBlur={() => runValidationTasks(\\"name\\", name)} + errorMessage={errors.name?.errorMessage} + hasError={errors.name?.hasError} + {...getOverrideProps(overrides, \\"name\\")} + > + { + let { value } = e.target; + if (onChange) { + const modelFields = { + name, + nmTest: value, + parentTable, + }; + const result = onChange(modelFields); + value = result?.nmTest ?? value; + } + if (errors.nmTest?.hasError) { + runValidationTasks(\\"nmTest\\", value); } - } - }} - {...getOverrideProps(overrides, \\"ClassUpdateForm\\")} - {...rest} - > + setNmTest(value); + }} + onBlur={() => runValidationTasks(\\"nmTest\\", nmTest)} + errorMessage={errors.nmTest?.errorMessage} + hasError={errors.nmTest?.hasError} + {...getOverrideProps(overrides, \\"nmTest\\")} + > { - let values = items; + let value = items[0]; if (onChange) { const modelFields = { - students: values, + name, + nmTest, + parentTable: value, }; const result = onChange(modelFields); - values = result?.students ?? values; + value = result?.parentTable ?? value; } - setStudents(values); - setCurrentStudentsValue(undefined); - setCurrentStudentsDisplayValue(\\"\\"); + setParentTable(value); + setCurrentParentTableValue(undefined); + setCurrentParentTableDisplayValue(\\"\\"); }} - currentFieldValue={currentStudentsValue} - label={\\"Students\\"} - items={students} - hasError={errors?.students?.hasError} + currentFieldValue={currentParentTableValue} + label={\\"Parent table\\"} + items={parentTable ? [parentTable] : []} + hasError={errors?.parentTable?.hasError} runValidationTasks={async () => - await runValidationTasks(\\"students\\", currentStudentsValue) + await runValidationTasks(\\"parentTable\\", currentParentTableValue) } - errorMessage={errors?.students?.errorMessage} - getBadgeText={getDisplayValue.students} + errorMessage={errors?.parentTable?.errorMessage} + getBadgeText={getDisplayValue.parentTable} setFieldValue={(model) => { - setCurrentStudentsDisplayValue( - model ? getDisplayValue.students(model) : \\"\\" + setCurrentParentTableDisplayValue( + model ? getDisplayValue.parentTable(model) : \\"\\" ); - setCurrentStudentsValue(model); + setCurrentParentTableValue(model); }} - inputFieldRef={studentsRef} + inputFieldRef={parentTableRef} defaultFieldValue={\\"\\"} > ({ - id: getIDValue.students?.(r), - label: getDisplayValue.students?.(r), - }))} - isLoading={studentsLoading} + placeholder=\\"Search ParentTable\\" + value={currentParentTableDisplayValue} + options={parentTableRecords + .filter((r) => !parentTableIdSet.has(getIDValue.parentTable?.(r))) + .map((r) => ({ + id: getIDValue.parentTable?.(r), + label: getDisplayValue.parentTable?.(r), + }))} + isLoading={parentTableLoading} onSelect={({ id, label }) => { - setCurrentStudentsValue( - studentsRecords.find((r) => + setCurrentParentTableValue( + parentTableRecords.find((r) => Object.entries(JSON.parse(id)).every( ([key, value]) => r[key] === value ) ) ); - setCurrentStudentsDisplayValue(label); - runValidationTasks(\\"students\\", label); + setCurrentParentTableDisplayValue(label); + runValidationTasks(\\"parentTable\\", label); }} onClear={() => { - setCurrentStudentsDisplayValue(\\"\\"); + setCurrentParentTableDisplayValue(\\"\\"); }} + defaultValue={parentTable} onChange={(e) => { let { value } = e.target; - fetchStudentsRecords(value); - if (errors.students?.hasError) { - runValidationTasks(\\"students\\", value); + fetchParentTableRecords(value); + if (errors.parentTable?.hasError) { + runValidationTasks(\\"parentTable\\", value); } - setCurrentStudentsDisplayValue(value); - setCurrentStudentsValue(undefined); + setCurrentParentTableDisplayValue(value); + setCurrentParentTableValue(undefined); }} onBlur={() => - runValidationTasks(\\"students\\", currentStudentsDisplayValue) + runValidationTasks(\\"parentTable\\", currentParentTableDisplayValue) } - errorMessage={errors.students?.errorMessage} - hasError={errors.students?.hasError} - ref={studentsRef} + errorMessage={errors.parentTable?.errorMessage} + hasError={errors.parentTable?.hasError} + ref={parentTableRef} labelHidden={true} - {...getOverrideProps(overrides, \\"students\\")} + {...getOverrideProps(overrides, \\"parentTable\\")} > e?.hasError) } {...getOverrideProps(overrides, \\"SubmitButton\\")} @@ -14451,43 +16441,49 @@ export default function ClassUpdateForm(props) { " `; -exports[`amplify form renderer tests GraphQL form tests should generate an update form with many to many relationship 2`] = ` +exports[`amplify form renderer tests GraphQL form tests should generate an update form with nonModel field 2`] = ` "import * as React from \\"react\\"; -import { AutocompleteProps, GridProps } from \\"@aws-amplify/ui-react\\"; +import { AutocompleteProps, GridProps, TextAreaFieldProps, TextFieldProps } from \\"@aws-amplify/ui-react\\"; import { EscapeHatchProps } from \\"@aws-amplify/ui-react/internal\\"; -import { Class, Student } from \\"../API\\"; +import { BasicTable, ParentTable } from \\"../API\\"; export declare type ValidationResponse = { hasError: boolean; errorMessage?: string; }; export declare type ValidationFunction = (value: T, validationResponse: ValidationResponse) => ValidationResponse | Promise; -export declare type ClassUpdateFormInputValues = { - students?: Student[]; +export declare type UpdateFormInputValues = { + name?: string; + nmTest?: string; + parentTable?: ParentTable; }; -export declare type ClassUpdateFormValidationValues = { - students?: ValidationFunction; +export declare type UpdateFormValidationValues = { + name?: ValidationFunction; + nmTest?: ValidationFunction; + parentTable?: ValidationFunction; }; export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes; -export declare type ClassUpdateFormOverridesProps = { - ClassUpdateFormGrid?: PrimitiveOverrideProps; - students?: PrimitiveOverrideProps; +export declare type UpdateFormOverridesProps = { + UpdateFormGrid?: PrimitiveOverrideProps; + name?: PrimitiveOverrideProps; + nmTest?: PrimitiveOverrideProps; + parentTable?: PrimitiveOverrideProps; } & EscapeHatchProps; -export declare type ClassUpdateFormProps = React.PropsWithChildren<{ - overrides?: ClassUpdateFormOverridesProps | undefined | null; +export declare type UpdateFormProps = React.PropsWithChildren<{ + overrides?: UpdateFormOverridesProps | undefined | null; } & { id?: string; - class?: Class; - onSubmit?: (fields: ClassUpdateFormInputValues) => ClassUpdateFormInputValues; - onSuccess?: (fields: ClassUpdateFormInputValues) => void; - onError?: (fields: ClassUpdateFormInputValues, errorMessage: string) => void; - onChange?: (fields: ClassUpdateFormInputValues) => ClassUpdateFormInputValues; - onValidate?: ClassUpdateFormValidationValues; + basicTable?: BasicTable; + onSubmit?: (fields: UpdateFormInputValues) => UpdateFormInputValues; + onSuccess?: (fields: UpdateFormInputValues) => void; + onError?: (fields: UpdateFormInputValues, errorMessage: string) => void; + onChange?: (fields: UpdateFormInputValues) => UpdateFormInputValues; + onValidate?: UpdateFormValidationValues; } & React.CSSProperties>; -export default function ClassUpdateForm(props: ClassUpdateFormProps): React.ReactElement; +export default function UpdateForm(props: UpdateFormProps): React.ReactElement; " `; -exports[`amplify form renderer tests GraphQL form tests should generate an update form with nonModel field 1`] = ` +exports[`amplify form renderer tests GraphQL form tests should generate an upgrade form with multiple relationship & cpk - amplify js v6 1`] = ` "/* eslint-disable */ import * as React from \\"react\\"; import { @@ -14500,15 +16496,27 @@ import { Icon, ScrollView, Text, - TextAreaField, TextField, useTheme, } from \\"@aws-amplify/ui-react\\"; import { getOverrideProps } from \\"@aws-amplify/ui-react/internal\\"; import { fetchByPath, validateField } from \\"./utils\\"; -import { API } from \\"aws-amplify\\"; -import { getBasicTable, listParentTables } from \\"../graphql/queries\\"; -import { updateBasicTable } from \\"../graphql/mutations\\"; +import { + cPKTeacherCPKClassesByCPKTeacherSpecialTeacherId, + getCPKTeacher, + listCPKClasses, + listCPKProjects, + listCPKStudents, + listCPKTeacherCPKClasses, +} from \\"../graphql/queries\\"; +import { generateClient } from \\"aws-amplify/api\\"; +import { + createCPKTeacherCPKClass, + deleteCPKTeacherCPKClass, + updateCPKProject, + updateCPKTeacher, +} from \\"../graphql/mutations\\"; +const client = generateClient(); function ArrayField({ items = [], onChange, @@ -14664,10 +16672,10 @@ function ArrayField({ ); } -export default function UpdateForm(props) { +export default function UpdateCPKTeacherForm(props) { const { - id: idProp, - basicTable: basicTableModelProp, + specialTeacherId: specialTeacherIdProp, + cPKTeacher: cPKTeacherModelProp, onSuccess, onError, onSubmit, @@ -14677,73 +16685,143 @@ export default function UpdateForm(props) { ...rest } = props; const initialValues = { - name: \\"\\", - nmTest: \\"\\", - parentTable: undefined, + specialTeacherId: \\"\\", + CPKStudent: undefined, + CPKClasses: [], + CPKProjects: [], }; - const [name, setName] = React.useState(initialValues.name); - const [nmTest, setNmTest] = React.useState(initialValues.nmTest); - const [parentTable, setParentTable] = React.useState( - initialValues.parentTable + const [specialTeacherId, setSpecialTeacherId] = React.useState( + initialValues.specialTeacherId ); - const [parentTableLoading, setParentTableLoading] = React.useState(false); - const [parentTableRecords, setParentTableRecords] = React.useState([]); + const [CPKStudent, setCPKStudent] = React.useState(initialValues.CPKStudent); + const [CPKStudentLoading, setCPKStudentLoading] = React.useState(false); + const [CPKStudentRecords, setCPKStudentRecords] = React.useState([]); + const [CPKClasses, setCPKClasses] = React.useState(initialValues.CPKClasses); + const [CPKClassesLoading, setCPKClassesLoading] = React.useState(false); + const [CPKClassesRecords, setCPKClassesRecords] = React.useState([]); + const [CPKProjects, setCPKProjects] = React.useState( + initialValues.CPKProjects + ); + const [CPKProjectsLoading, setCPKProjectsLoading] = React.useState(false); + const [CPKProjectsRecords, setCPKProjectsRecords] = React.useState([]); const autocompleteLength = 10; const [errors, setErrors] = React.useState({}); const resetStateValues = () => { - const cleanValues = basicTableRecord - ? { ...initialValues, ...basicTableRecord, parentTable } + const cleanValues = cPKTeacherRecord + ? { + ...initialValues, + ...cPKTeacherRecord, + CPKStudent, + CPKClasses: linkedCPKClasses, + CPKProjects: linkedCPKProjects, + } : initialValues; - setName(cleanValues.name); - setNmTest( - typeof cleanValues.nmTest === \\"string\\" || cleanValues.nmTest === null - ? cleanValues.nmTest - : JSON.stringify(cleanValues.nmTest) - ); - setParentTable(cleanValues.parentTable); - setCurrentParentTableValue(undefined); - setCurrentParentTableDisplayValue(\\"\\"); + setSpecialTeacherId(cleanValues.specialTeacherId); + setCPKStudent(cleanValues.CPKStudent); + setCurrentCPKStudentValue(undefined); + setCurrentCPKStudentDisplayValue(\\"\\"); + setCPKClasses(cleanValues.CPKClasses ?? []); + setCurrentCPKClassesValue(undefined); + setCurrentCPKClassesDisplayValue(\\"\\"); + setCPKProjects(cleanValues.CPKProjects ?? []); + setCurrentCPKProjectsValue(undefined); + setCurrentCPKProjectsDisplayValue(\\"\\"); setErrors({}); }; - const [basicTableRecord, setBasicTableRecord] = - React.useState(basicTableModelProp); + const [cPKTeacherRecord, setCPKTeacherRecord] = + React.useState(cPKTeacherModelProp); + const [linkedCPKClasses, setLinkedCPKClasses] = React.useState([]); + const canUnlinkCPKClasses = false; + const [linkedCPKProjects, setLinkedCPKProjects] = React.useState([]); + const canUnlinkCPKProjects = true; React.useEffect(() => { const queryData = async () => { - const record = idProp + const record = specialTeacherIdProp ? ( - await API.graphql({ - query: getBasicTable.replaceAll(\\"__typename\\", \\"\\"), - variables: { id: idProp }, + await client.graphql({ + query: getCPKTeacher.replaceAll(\\"__typename\\", \\"\\"), + variables: { specialTeacherId: specialTeacherIdProp }, }) - )?.data?.getBasicTable - : basicTableModelProp; - const parentTableRecord = record ? await record.parentTable : undefined; - setParentTable(parentTableRecord); - setBasicTableRecord(record); + )?.data?.getCPKTeacher + : cPKTeacherModelProp; + const CPKStudentRecord = record ? await record.CPKStudent : undefined; + setCPKStudent(CPKStudentRecord); + const linkedCPKClasses = record + ? ( + await client.graphql({ + query: + cPKTeacherCPKClassesByCPKTeacherSpecialTeacherId.replaceAll( + \\"__typename\\", + \\"\\" + ), + variables: { + cPKTeacherSpecialTeacherId: record.specialTeacherId, + }, + }) + ).data.cPKTeacherCPKClassesByCPKTeacherSpecialTeacherId.items.map( + (t) => t.cpkClass + ) + : []; + setLinkedCPKClasses(linkedCPKClasses); + const linkedCPKProjects = record?.CPKProjects?.items ?? []; + setLinkedCPKProjects(linkedCPKProjects); + setCPKTeacherRecord(record); }; queryData(); - }, [idProp, basicTableModelProp]); - React.useEffect(resetStateValues, [basicTableRecord, parentTable]); - const [currentParentTableDisplayValue, setCurrentParentTableDisplayValue] = + }, [specialTeacherIdProp, cPKTeacherModelProp]); + React.useEffect(resetStateValues, [ + cPKTeacherRecord, + CPKStudent, + linkedCPKClasses, + linkedCPKProjects, + ]); + const [currentCPKStudentDisplayValue, setCurrentCPKStudentDisplayValue] = React.useState(\\"\\"); - const [currentParentTableValue, setCurrentParentTableValue] = + const [currentCPKStudentValue, setCurrentCPKStudentValue] = React.useState(undefined); - const parentTableRef = React.createRef(); + const CPKStudentRef = React.createRef(); + const [currentCPKClassesDisplayValue, setCurrentCPKClassesDisplayValue] = + React.useState(\\"\\"); + const [currentCPKClassesValue, setCurrentCPKClassesValue] = + React.useState(undefined); + const CPKClassesRef = React.createRef(); + const [currentCPKProjectsDisplayValue, setCurrentCPKProjectsDisplayValue] = + React.useState(\\"\\"); + const [currentCPKProjectsValue, setCurrentCPKProjectsValue] = + React.useState(undefined); + const CPKProjectsRef = React.createRef(); const getIDValue = { - parentTable: (r) => JSON.stringify({ id: r?.id }), + CPKStudent: (r) => + JSON.stringify({ specialStudentId: r?.specialStudentId }), + CPKClasses: (r) => JSON.stringify({ specialClassId: r?.specialClassId }), + CPKProjects: (r) => + JSON.stringify({ specialProjectId: r?.specialProjectId }), }; - const parentTableIdSet = new Set( - Array.isArray(parentTable) - ? parentTable.map((r) => getIDValue.parentTable?.(r)) - : getIDValue.parentTable?.(parentTable) + const CPKStudentIdSet = new Set( + Array.isArray(CPKStudent) + ? CPKStudent.map((r) => getIDValue.CPKStudent?.(r)) + : getIDValue.CPKStudent?.(CPKStudent) + ); + const CPKClassesIdSet = new Set( + Array.isArray(CPKClasses) + ? CPKClasses.map((r) => getIDValue.CPKClasses?.(r)) + : getIDValue.CPKClasses?.(CPKClasses) + ); + const CPKProjectsIdSet = new Set( + Array.isArray(CPKProjects) + ? CPKProjects.map((r) => getIDValue.CPKProjects?.(r)) + : getIDValue.CPKProjects?.(CPKProjects) ); const getDisplayValue = { - parentTable: (r) => \`\${r?.name ? r?.name + \\" - \\" : \\"\\"}\${r?.id}\`, + CPKStudent: (r) => r?.specialStudentId, + CPKClasses: (r) => r?.specialClassId, + CPKProjects: (r) => r?.specialProjectId, }; const validations = { - name: [], - nmTest: [{ type: \\"JSON\\" }], - parentTable: [], + specialTeacherId: [{ type: \\"Required\\" }], + CPKStudent: [], + CPKClasses: [], + CPKProjects: [], }; const runValidationTasks = async ( fieldName, @@ -14762,37 +16840,91 @@ export default function UpdateForm(props) { setErrors((errors) => ({ ...errors, [fieldName]: validationResponse })); return validationResponse; }; - const fetchParentTableRecords = async (value) => { - setParentTableLoading(true); + const fetchCPKStudentRecords = async (value) => { + setCPKStudentLoading(true); const newOptions = []; let newNext = \\"\\"; while (newOptions.length < autocompleteLength && newNext != null) { const variables = { limit: autocompleteLength * 5, - filter: { - or: [{ name: { contains: value } }, { id: { contains: value } }], - }, + filter: { or: [{ specialStudentId: { contains: value } }] }, }; if (newNext) { variables[\\"nextToken\\"] = newNext; } const result = ( - await API.graphql({ - query: listParentTables.replaceAll(\\"__typename\\", \\"\\"), + await client.graphql({ + query: listCPKStudents.replaceAll(\\"__typename\\", \\"\\"), variables, }) - )?.data?.listParentTables?.items; + )?.data?.listCPKStudents?.items; var loaded = result.filter( - (item) => !parentTableIdSet.has(getIDValue.parentTable?.(item)) + (item) => !CPKStudentIdSet.has(getIDValue.CPKStudent?.(item)) ); newOptions.push(...loaded); newNext = result.nextToken; } - setParentTableRecords(newOptions.slice(0, autocompleteLength)); - setParentTableLoading(false); + setCPKStudentRecords(newOptions.slice(0, autocompleteLength)); + setCPKStudentLoading(false); + }; + const fetchCPKClassesRecords = async (value) => { + setCPKClassesLoading(true); + const newOptions = []; + let newNext = \\"\\"; + while (newOptions.length < autocompleteLength && newNext != null) { + const variables = { + limit: autocompleteLength * 5, + filter: { or: [{ specialClassId: { contains: value } }] }, + }; + if (newNext) { + variables[\\"nextToken\\"] = newNext; + } + const result = ( + await client.graphql({ + query: listCPKClasses.replaceAll(\\"__typename\\", \\"\\"), + variables, + }) + )?.data?.listCPKClasses?.items; + var loaded = result.filter( + (item) => !CPKClassesIdSet.has(getIDValue.CPKClasses?.(item)) + ); + newOptions.push(...loaded); + newNext = result.nextToken; + } + setCPKClassesRecords(newOptions.slice(0, autocompleteLength)); + setCPKClassesLoading(false); + }; + const fetchCPKProjectsRecords = async (value) => { + setCPKProjectsLoading(true); + const newOptions = []; + let newNext = \\"\\"; + while (newOptions.length < autocompleteLength && newNext != null) { + const variables = { + limit: autocompleteLength * 5, + filter: { or: [{ specialProjectId: { contains: value } }] }, + }; + if (newNext) { + variables[\\"nextToken\\"] = newNext; + } + const result = ( + await client.graphql({ + query: listCPKProjects.replaceAll(\\"__typename\\", \\"\\"), + variables, + }) + )?.data?.listCPKProjects?.items; + var loaded = result.filter( + (item) => !CPKProjectsIdSet.has(getIDValue.CPKProjects?.(item)) + ); + newOptions.push(...loaded); + newNext = result.nextToken; + } + setCPKProjectsRecords(newOptions.slice(0, autocompleteLength)); + setCPKProjectsLoading(false); }; React.useEffect(() => { - fetchParentTableRecords(\\"\\"); + fetchCPKStudentRecords(\\"\\"); + fetchCPKClassesRecords(\\"\\"); + fetchCPKProjectsRecords(\\"\\"); }, []); return ( { event.preventDefault(); let modelFields = { - name: name ?? null, - nmTest: nmTest ?? null, - parentTable: parentTable ?? null, + specialTeacherId, + CPKStudent: CPKStudent ?? null, + CPKClasses: CPKClasses ?? null, + CPKProjects: CPKProjects ?? null, }; const validationResponses = await Promise.all( Object.keys(validations).reduce((promises, fieldName) => { if (Array.isArray(modelFields[fieldName])) { promises.push( - ...modelFields[fieldName].map((item) => - runValidationTasks( - fieldName, - item, - getDisplayValue[fieldName] - ) - ) + ...modelFields[fieldName].map((item) => + runValidationTasks( + fieldName, + item, + getDisplayValue[fieldName] + ) + ) + ); + return promises; + } + promises.push( + runValidationTasks( + fieldName, + modelFields[fieldName], + getDisplayValue[fieldName] + ) + ); + return promises; + }, []) + ); + if (validationResponses.some((r) => r.hasError)) { + return; + } + if (onSubmit) { + modelFields = onSubmit(modelFields); + } + try { + Object.entries(modelFields).forEach(([key, value]) => { + if (typeof value === \\"string\\" && value === \\"\\") { + modelFields[key] = null; + } + }); + const promises = []; + const cPKClassesToLinkMap = new Map(); + const cPKClassesToUnLinkMap = new Map(); + const cPKClassesMap = new Map(); + const linkedCPKClassesMap = new Map(); + CPKClasses.forEach((r) => { + const count = cPKClassesMap.get(getIDValue.CPKClasses?.(r)); + const newCount = count ? count + 1 : 1; + cPKClassesMap.set(getIDValue.CPKClasses?.(r), newCount); + }); + linkedCPKClasses.forEach((r) => { + const count = linkedCPKClassesMap.get(getIDValue.CPKClasses?.(r)); + const newCount = count ? count + 1 : 1; + linkedCPKClassesMap.set(getIDValue.CPKClasses?.(r), newCount); + }); + linkedCPKClassesMap.forEach((count, id) => { + const newCount = cPKClassesMap.get(id); + if (newCount) { + const diffCount = count - newCount; + if (diffCount > 0) { + cPKClassesToUnLinkMap.set(id, diffCount); + } + } else { + cPKClassesToUnLinkMap.set(id, count); + } + }); + cPKClassesMap.forEach((count, id) => { + const originalCount = linkedCPKClassesMap.get(id); + if (originalCount) { + const diffCount = count - originalCount; + if (diffCount > 0) { + cPKClassesToLinkMap.set(id, diffCount); + } + } else { + cPKClassesToLinkMap.set(id, count); + } + }); + cPKClassesToUnLinkMap.forEach(async (count, id) => { + const recordKeys = JSON.parse(id); + const cPKTeacherCPKClassRecords = ( + await client.graphql({ + query: listCPKTeacherCPKClasses.replaceAll(\\"__typename\\", \\"\\"), + variables: { + filter: { + and: [ + { + cPKClassSpecialClassId: { + eq: recordKeys.specialClassId, + }, + }, + { + cPKTeacherSpecialTeacherId: { + eq: cPKTeacherRecord.specialTeacherId, + }, + }, + ], + }, + }, + }) + )?.data?.listCPKTeacherCPKClasses?.items; + for (let i = 0; i < count; i++) { + promises.push( + client.graphql({ + query: deleteCPKTeacherCPKClass.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + id: cPKTeacherCPKClassRecords[i].id, + }, + }, + }) ); - return promises; } - promises.push( - runValidationTasks( - fieldName, - modelFields[fieldName], - getDisplayValue[fieldName] + }); + cPKClassesToLinkMap.forEach((count, id) => { + const cPKClassToLink = cPKClassRecords.find((r) => + Object.entries(JSON.parse(id)).every( + ([key, value]) => r[key] === value ) ); - return promises; - }, []) - ); - if (validationResponses.some((r) => r.hasError)) { - return; - } - if (onSubmit) { - modelFields = onSubmit(modelFields); - } - try { - Object.entries(modelFields).forEach(([key, value]) => { - if (typeof value === \\"string\\" && value === \\"\\") { - modelFields[key] = null; + for (let i = count; i > 0; i--) { + promises.push( + client.graphql({ + query: createCPKTeacherCPKClass.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + cPKTeacherSpecialTeacherId: + cPKTeacherRecord.specialTeacherId, + cPKClassSpecialClassId: cPKClassToLink.specialClassId, + }, + }, + }) + ); + } + }); + const cPKProjectsToLink = []; + const cPKProjectsToUnLink = []; + const cPKProjectsSet = new Set(); + const linkedCPKProjectsSet = new Set(); + CPKProjects.forEach((r) => + cPKProjectsSet.add(getIDValue.CPKProjects?.(r)) + ); + linkedCPKProjects.forEach((r) => + linkedCPKProjectsSet.add(getIDValue.CPKProjects?.(r)) + ); + linkedCPKProjects.forEach((r) => { + if (!cPKProjectsSet.has(getIDValue.CPKProjects?.(r))) { + cPKProjectsToUnLink.push(r); + } + }); + CPKProjects.forEach((r) => { + if (!linkedCPKProjectsSet.has(getIDValue.CPKProjects?.(r))) { + cPKProjectsToLink.push(r); + } + }); + cPKProjectsToUnLink.forEach((original) => { + if (!canUnlinkCPKProjects) { + throw Error( + \`CPKProject \${original.specialProjectId} cannot be unlinked from CPKTeacher because cPKTeacherID is a required field.\` + ); } + promises.push( + client.graphql({ + query: updateCPKProject.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + specialProjectId: original.specialProjectId, + cPKTeacherID: null, + }, + }, + }) + ); + }); + cPKProjectsToLink.forEach((original) => { + promises.push( + client.graphql({ + query: updateCPKProject.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + specialProjectId: original.specialProjectId, + cPKTeacherID: cPKTeacherRecord.specialTeacherId, + }, + }, + }) + ); }); const modelFieldsToSave = { - name: modelFields.name ?? null, - parentTableBasicTablesId: modelFields?.parentTable?.id ?? null, - nmTest: modelFields.nmTest - ? JSON.parse(modelFields.nmTest) - : modelFields.nmTest, + specialTeacherId: modelFields.specialTeacherId, + cPKTeacherCPKStudentSpecialStudentId: + modelFields?.CPKStudent?.specialStudentId ?? null, }; - await API.graphql({ - query: updateBasicTable.replaceAll(\\"__typename\\", \\"\\"), - variables: { - input: { - id: basicTableRecord.id, - ...modelFieldsToSave, + promises.push( + client.graphql({ + query: updateCPKTeacher.replaceAll(\\"__typename\\", \\"\\"), + variables: { + input: { + specialTeacherId: cPKTeacherRecord.specialTeacherId, + ...modelFieldsToSave, + }, }, - }, - }); + }) + ); + await Promise.all(promises); if (onSuccess) { onSuccess(modelFields); } @@ -14869,141 +17148,277 @@ export default function UpdateForm(props) { } } }} - {...getOverrideProps(overrides, \\"UpdateForm\\")} + {...getOverrideProps(overrides, \\"UpdateCPKTeacherForm\\")} {...rest} > { let { value } = e.target; if (onChange) { const modelFields = { - name: value, - nmTest, - parentTable, + specialTeacherId: value, + CPKStudent, + CPKClasses, + CPKProjects, }; const result = onChange(modelFields); - value = result?.name ?? value; + value = result?.specialTeacherId ?? value; } - if (errors.name?.hasError) { - runValidationTasks(\\"name\\", value); + if (errors.specialTeacherId?.hasError) { + runValidationTasks(\\"specialTeacherId\\", value); + } + setSpecialTeacherId(value); + }} + onBlur={() => runValidationTasks(\\"specialTeacherId\\", specialTeacherId)} + errorMessage={errors.specialTeacherId?.errorMessage} + hasError={errors.specialTeacherId?.hasError} + {...getOverrideProps(overrides, \\"specialTeacherId\\")} + > + { + let value = items[0]; + if (onChange) { + const modelFields = { + specialTeacherId, + CPKStudent: value, + CPKClasses, + CPKProjects, + }; + const result = onChange(modelFields); + value = result?.CPKStudent ?? value; + } + setCPKStudent(value); + setCurrentCPKStudentValue(undefined); + setCurrentCPKStudentDisplayValue(\\"\\"); + }} + currentFieldValue={currentCPKStudentValue} + label={\\"Cpk student\\"} + items={CPKStudent ? [CPKStudent] : []} + hasError={errors?.CPKStudent?.hasError} + runValidationTasks={async () => + await runValidationTasks(\\"CPKStudent\\", currentCPKStudentValue) + } + errorMessage={errors?.CPKStudent?.errorMessage} + getBadgeText={getDisplayValue.CPKStudent} + setFieldValue={(model) => { + setCurrentCPKStudentDisplayValue( + model ? getDisplayValue.CPKStudent(model) : \\"\\" + ); + setCurrentCPKStudentValue(model); + }} + inputFieldRef={CPKStudentRef} + defaultFieldValue={\\"\\"} + > + !CPKStudentIdSet.has(getIDValue.CPKStudent?.(r)) + ).map((r) => ({ + id: getIDValue.CPKStudent?.(r), + label: getDisplayValue.CPKStudent?.(r), + }))} + isLoading={CPKStudentLoading} + onSelect={({ id, label }) => { + setCurrentCPKStudentValue( + CPKStudentRecords.find((r) => + Object.entries(JSON.parse(id)).every( + ([key, value]) => r[key] === value + ) + ) + ); + setCurrentCPKStudentDisplayValue(label); + runValidationTasks(\\"CPKStudent\\", label); + }} + onClear={() => { + setCurrentCPKStudentDisplayValue(\\"\\"); + }} + defaultValue={CPKStudent} + onChange={(e) => { + let { value } = e.target; + fetchCPKStudentRecords(value); + if (errors.CPKStudent?.hasError) { + runValidationTasks(\\"CPKStudent\\", value); + } + setCurrentCPKStudentDisplayValue(value); + setCurrentCPKStudentValue(undefined); + }} + onBlur={() => + runValidationTasks(\\"CPKStudent\\", currentCPKStudentDisplayValue) } - setName(value); - }} - onBlur={() => runValidationTasks(\\"name\\", name)} - errorMessage={errors.name?.errorMessage} - hasError={errors.name?.hasError} - {...getOverrideProps(overrides, \\"name\\")} - > - { - let { value } = e.target; + errorMessage={errors.CPKStudent?.errorMessage} + hasError={errors.CPKStudent?.hasError} + ref={CPKStudentRef} + labelHidden={true} + {...getOverrideProps(overrides, \\"CPKStudent\\")} + > + + { + let values = items; if (onChange) { const modelFields = { - name, - nmTest: value, - parentTable, + specialTeacherId, + CPKStudent, + CPKClasses: values, + CPKProjects, }; const result = onChange(modelFields); - value = result?.nmTest ?? value; - } - if (errors.nmTest?.hasError) { - runValidationTasks(\\"nmTest\\", value); + values = result?.CPKClasses ?? values; } - setNmTest(value); + setCPKClasses(values); + setCurrentCPKClassesValue(undefined); + setCurrentCPKClassesDisplayValue(\\"\\"); }} - onBlur={() => runValidationTasks(\\"nmTest\\", nmTest)} - errorMessage={errors.nmTest?.errorMessage} - hasError={errors.nmTest?.hasError} - {...getOverrideProps(overrides, \\"nmTest\\")} - > + currentFieldValue={currentCPKClassesValue} + label={\\"Cpk classes\\"} + items={CPKClasses} + hasError={errors?.CPKClasses?.hasError} + runValidationTasks={async () => + await runValidationTasks(\\"CPKClasses\\", currentCPKClassesValue) + } + errorMessage={errors?.CPKClasses?.errorMessage} + getBadgeText={getDisplayValue.CPKClasses} + setFieldValue={(model) => { + setCurrentCPKClassesDisplayValue( + model ? getDisplayValue.CPKClasses(model) : \\"\\" + ); + setCurrentCPKClassesValue(model); + }} + inputFieldRef={CPKClassesRef} + defaultFieldValue={\\"\\"} + > + ({ + id: getIDValue.CPKClasses?.(r), + label: getDisplayValue.CPKClasses?.(r), + }))} + isLoading={CPKClassesLoading} + onSelect={({ id, label }) => { + setCurrentCPKClassesValue( + CPKClassesRecords.find((r) => + Object.entries(JSON.parse(id)).every( + ([key, value]) => r[key] === value + ) + ) + ); + setCurrentCPKClassesDisplayValue(label); + runValidationTasks(\\"CPKClasses\\", label); + }} + onClear={() => { + setCurrentCPKClassesDisplayValue(\\"\\"); + }} + onChange={(e) => { + let { value } = e.target; + fetchCPKClassesRecords(value); + if (errors.CPKClasses?.hasError) { + runValidationTasks(\\"CPKClasses\\", value); + } + setCurrentCPKClassesDisplayValue(value); + setCurrentCPKClassesValue(undefined); + }} + onBlur={() => + runValidationTasks(\\"CPKClasses\\", currentCPKClassesDisplayValue) + } + errorMessage={errors.CPKClasses?.errorMessage} + hasError={errors.CPKClasses?.hasError} + ref={CPKClassesRef} + labelHidden={true} + {...getOverrideProps(overrides, \\"CPKClasses\\")} + > + { - let value = items[0]; + let values = items; if (onChange) { const modelFields = { - name, - nmTest, - parentTable: value, + specialTeacherId, + CPKStudent, + CPKClasses, + CPKProjects: values, }; const result = onChange(modelFields); - value = result?.parentTable ?? value; + values = result?.CPKProjects ?? values; } - setParentTable(value); - setCurrentParentTableValue(undefined); - setCurrentParentTableDisplayValue(\\"\\"); + setCPKProjects(values); + setCurrentCPKProjectsValue(undefined); + setCurrentCPKProjectsDisplayValue(\\"\\"); }} - currentFieldValue={currentParentTableValue} - label={\\"Parent table\\"} - items={parentTable ? [parentTable] : []} - hasError={errors?.parentTable?.hasError} + currentFieldValue={currentCPKProjectsValue} + label={\\"Cpk projects\\"} + items={CPKProjects} + hasError={errors?.CPKProjects?.hasError} runValidationTasks={async () => - await runValidationTasks(\\"parentTable\\", currentParentTableValue) + await runValidationTasks(\\"CPKProjects\\", currentCPKProjectsValue) } - errorMessage={errors?.parentTable?.errorMessage} - getBadgeText={getDisplayValue.parentTable} + errorMessage={errors?.CPKProjects?.errorMessage} + getBadgeText={getDisplayValue.CPKProjects} setFieldValue={(model) => { - setCurrentParentTableDisplayValue( - model ? getDisplayValue.parentTable(model) : \\"\\" + setCurrentCPKProjectsDisplayValue( + model ? getDisplayValue.CPKProjects(model) : \\"\\" ); - setCurrentParentTableValue(model); + setCurrentCPKProjectsValue(model); }} - inputFieldRef={parentTableRef} + inputFieldRef={CPKProjectsRef} defaultFieldValue={\\"\\"} > !parentTableIdSet.has(getIDValue.parentTable?.(r))) - .map((r) => ({ - id: getIDValue.parentTable?.(r), - label: getDisplayValue.parentTable?.(r), - }))} - isLoading={parentTableLoading} + placeholder=\\"Search CPKProject\\" + value={currentCPKProjectsDisplayValue} + options={CPKProjectsRecords.filter( + (r) => !CPKProjectsIdSet.has(getIDValue.CPKProjects?.(r)) + ).map((r) => ({ + id: getIDValue.CPKProjects?.(r), + label: getDisplayValue.CPKProjects?.(r), + }))} + isLoading={CPKProjectsLoading} onSelect={({ id, label }) => { - setCurrentParentTableValue( - parentTableRecords.find((r) => + setCurrentCPKProjectsValue( + CPKProjectsRecords.find((r) => Object.entries(JSON.parse(id)).every( ([key, value]) => r[key] === value ) ) ); - setCurrentParentTableDisplayValue(label); - runValidationTasks(\\"parentTable\\", label); + setCurrentCPKProjectsDisplayValue(label); + runValidationTasks(\\"CPKProjects\\", label); }} onClear={() => { - setCurrentParentTableDisplayValue(\\"\\"); + setCurrentCPKProjectsDisplayValue(\\"\\"); }} - defaultValue={parentTable} onChange={(e) => { let { value } = e.target; - fetchParentTableRecords(value); - if (errors.parentTable?.hasError) { - runValidationTasks(\\"parentTable\\", value); + fetchCPKProjectsRecords(value); + if (errors.CPKProjects?.hasError) { + runValidationTasks(\\"CPKProjects\\", value); } - setCurrentParentTableDisplayValue(value); - setCurrentParentTableValue(undefined); + setCurrentCPKProjectsDisplayValue(value); + setCurrentCPKProjectsValue(undefined); }} onBlur={() => - runValidationTasks(\\"parentTable\\", currentParentTableDisplayValue) + runValidationTasks(\\"CPKProjects\\", currentCPKProjectsDisplayValue) } - errorMessage={errors.parentTable?.errorMessage} - hasError={errors.parentTable?.hasError} - ref={parentTableRef} + errorMessage={errors.CPKProjects?.errorMessage} + hasError={errors.CPKProjects?.hasError} + ref={CPKProjectsRef} labelHidden={true} - {...getOverrideProps(overrides, \\"parentTable\\")} + {...getOverrideProps(overrides, \\"CPKProjects\\")} > e?.hasError) } {...getOverrideProps(overrides, \\"SubmitButton\\")} @@ -15042,45 +17457,48 @@ export default function UpdateForm(props) { " `; -exports[`amplify form renderer tests GraphQL form tests should generate an update form with nonModel field 2`] = ` +exports[`amplify form renderer tests GraphQL form tests should generate an upgrade form with multiple relationship & cpk - amplify js v6 2`] = ` "import * as React from \\"react\\"; -import { AutocompleteProps, GridProps, TextAreaFieldProps, TextFieldProps } from \\"@aws-amplify/ui-react\\"; +import { AutocompleteProps, GridProps, TextFieldProps } from \\"@aws-amplify/ui-react\\"; import { EscapeHatchProps } from \\"@aws-amplify/ui-react/internal\\"; -import { BasicTable, ParentTable } from \\"../API\\"; +import { CPKClass, CPKProject, CPKStudent, CPKTeacher } from \\"../API\\"; export declare type ValidationResponse = { hasError: boolean; errorMessage?: string; }; export declare type ValidationFunction = (value: T, validationResponse: ValidationResponse) => ValidationResponse | Promise; -export declare type UpdateFormInputValues = { - name?: string; - nmTest?: string; - parentTable?: ParentTable; +export declare type UpdateCPKTeacherFormInputValues = { + specialTeacherId?: string; + CPKStudent?: CPKStudent; + CPKClasses?: CPKClass[]; + CPKProjects?: CPKProject[]; }; -export declare type UpdateFormValidationValues = { - name?: ValidationFunction; - nmTest?: ValidationFunction; - parentTable?: ValidationFunction; +export declare type UpdateCPKTeacherFormValidationValues = { + specialTeacherId?: ValidationFunction; + CPKStudent?: ValidationFunction; + CPKClasses?: ValidationFunction; + CPKProjects?: ValidationFunction; }; export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes; -export declare type UpdateFormOverridesProps = { - UpdateFormGrid?: PrimitiveOverrideProps; - name?: PrimitiveOverrideProps; - nmTest?: PrimitiveOverrideProps; - parentTable?: PrimitiveOverrideProps; +export declare type UpdateCPKTeacherFormOverridesProps = { + UpdateCPKTeacherFormGrid?: PrimitiveOverrideProps; + specialTeacherId?: PrimitiveOverrideProps; + CPKStudent?: PrimitiveOverrideProps; + CPKClasses?: PrimitiveOverrideProps; + CPKProjects?: PrimitiveOverrideProps; } & EscapeHatchProps; -export declare type UpdateFormProps = React.PropsWithChildren<{ - overrides?: UpdateFormOverridesProps | undefined | null; +export declare type UpdateCPKTeacherFormProps = React.PropsWithChildren<{ + overrides?: UpdateCPKTeacherFormOverridesProps | undefined | null; } & { - id?: string; - basicTable?: BasicTable; - onSubmit?: (fields: UpdateFormInputValues) => UpdateFormInputValues; - onSuccess?: (fields: UpdateFormInputValues) => void; - onError?: (fields: UpdateFormInputValues, errorMessage: string) => void; - onChange?: (fields: UpdateFormInputValues) => UpdateFormInputValues; - onValidate?: UpdateFormValidationValues; + specialTeacherId?: string; + cPKTeacher?: CPKTeacher; + onSubmit?: (fields: UpdateCPKTeacherFormInputValues) => UpdateCPKTeacherFormInputValues; + onSuccess?: (fields: UpdateCPKTeacherFormInputValues) => void; + onError?: (fields: UpdateCPKTeacherFormInputValues, errorMessage: string) => void; + onChange?: (fields: UpdateCPKTeacherFormInputValues) => UpdateCPKTeacherFormInputValues; + onValidate?: UpdateCPKTeacherFormValidationValues; } & React.CSSProperties>; -export default function UpdateForm(props: UpdateFormProps): React.ReactElement; +export default function UpdateCPKTeacherForm(props: UpdateCPKTeacherFormProps): React.ReactElement; " `; diff --git a/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react-forms.test.ts b/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react-forms.test.ts index adbce169..bce2db1c 100644 --- a/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react-forms.test.ts +++ b/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react-forms.test.ts @@ -911,6 +911,32 @@ describe('amplify form renderer tests', () => { expect(declaration).toMatchSnapshot(); }); + it('should generate an update form with many to many relationship - amplify js v6', () => { + const { componentText, declaration } = generateWithAmplifyFormRenderer( + 'forms/relationships/update-class', + 'datastore/relationships/many-to-many-class', + { ...defaultCLIRenderConfig, ...rendererConfigWithGraphQL, dependencies: { 'aws-amplify': '^6.0.0' } }, + { isNonModelSupported: true, isRelationshipSupported: true }, + ); + + // check for import statement for graphql operation + // v6 api + expect(componentText).toContain('import { generateClient } from "aws-amplify/api";'); + expect(componentText).toContain(`const client = generateClient();`); + expect(componentText).toContain('await client.graphql({'); + + expect(componentText).not.toContain('DataStore'); + expect(componentText).not.toContain('import { API } from "aws-amplify";'); + expect(componentText).not.toContain(`API.graphql`); + + expect(componentText).toContain('createStudentClass'); + expect(componentText).toContain('deleteStudentClass'); + expect(componentText).toContain('updateClass'); + + expect(componentText).toMatchSnapshot(); + expect(declaration).toMatchSnapshot(); + }); + it('should generate an update form with composite primary key', () => { const { componentText, declaration } = generateWithAmplifyFormRenderer( 'forms/relationships/update-movie', @@ -939,6 +965,30 @@ describe('amplify form renderer tests', () => { expect(declaration).toMatchSnapshot(); }); + it('should generate an update form with composite primary key - amplify js v6', () => { + const { componentText, declaration } = generateWithAmplifyFormRenderer( + 'forms/relationships/update-movie', + 'models/composite-key-movie', + { ...defaultCLIRenderConfig, ...rendererConfigWithGraphQL, dependencies: { 'aws-amplify': '^6.0.0' } }, + { isNonModelSupported: true, isRelationshipSupported: true }, + ); + + // no datastore reference + expect(componentText).not.toContain('DataStore'); + + // no v5 api references + expect(componentText).not.toContain('import { API } from "aws-amplify";'); + expect(componentText).not.toContain(`API.graphql`); + + // v6 api + expect(componentText).toContain('import { generateClient } from "aws-amplify/api";'); + expect(componentText).toContain(`const client = generateClient();`); + expect(componentText).toContain('await client.graphql({'); + + expect(componentText).toMatchSnapshot(); + expect(declaration).toMatchSnapshot(); + }); + it('should generate an upgrade form with multiple relationship & cpk', () => { const { componentText, declaration } = generateWithAmplifyFormRenderer( 'forms/cpk-teacher-datastore-update', @@ -971,6 +1021,29 @@ describe('amplify form renderer tests', () => { expect(declaration).toMatchSnapshot(); }); + it('should generate an upgrade form with multiple relationship & cpk - amplify js v6', () => { + const { componentText, declaration } = generateWithAmplifyFormRenderer( + 'forms/cpk-teacher-datastore-update', + 'datastore/cpk-relationships', + { ...defaultCLIRenderConfig, ...rendererConfigWithGraphQL, dependencies: { 'aws-amplify': '^6.0.0' } }, + { isNonModelSupported: true, isRelationshipSupported: true }, + ); + + // check for import statement for graphql operation + expect(componentText).not.toContain('DataStore'); + // no v5 api references + expect(componentText).not.toContain('import { API } from "aws-amplify";'); + expect(componentText).not.toContain(`API.graphql`); + + // v6 api + expect(componentText).toContain('import { generateClient } from "aws-amplify/api";'); + expect(componentText).toContain(`const client = generateClient();`); + expect(componentText).toContain('await client.graphql({'); + + expect(componentText).toMatchSnapshot(); + expect(declaration).toMatchSnapshot(); + }); + it('should generate a create form with multiple hasOne relationships', () => { const { componentText, declaration } = generateWithAmplifyFormRenderer( 'forms/book-datastore-relationship-multiple', @@ -1001,6 +1074,26 @@ describe('amplify form renderer tests', () => { expect(declaration).toMatchSnapshot(); }); + it('should generate a create form with belongsTo relationship - amplify js v6', () => { + const { componentText } = generateWithAmplifyFormRenderer( + 'forms/member-datastore-create', + 'datastore/project-team-model', + { ...defaultCLIRenderConfig, ...rendererConfigWithGraphQL, dependencies: { 'aws-amplify': '^6.0.0' } }, + { isNonModelSupported: true, isRelationshipSupported: true }, + ); + + // check for import statement for graphql operation + expect(componentText).not.toContain('DataStore'); + // no v5 api references + expect(componentText).not.toContain('import { API } from "aws-amplify";'); + expect(componentText).not.toContain(`API.graphql`); + + // v6 api + expect(componentText).toContain('import { generateClient } from "aws-amplify/api";'); + expect(componentText).toContain(`const client = generateClient();`); + expect(componentText).toContain('await client.graphql({'); + }); + it('should generate a create form with nonModel field', () => { const { componentText, declaration } = generateWithAmplifyFormRenderer( 'models/table-with-custom-type/forms/BasicTableCreateFormDefault', @@ -1108,6 +1201,24 @@ describe('amplify form renderer tests', () => { expect(declaration).toMatchSnapshot(); }); + it('should render a create form for child of 1:m relationship - amplify js v6', () => { + const { componentText } = generateWithAmplifyFormRenderer( + 'forms/composite-toy-datastore-create', + 'datastore/composite-relationships', + { ...defaultCLIRenderConfig, ...rendererConfigWithGraphQL, dependencies: { 'aws-amplify': '^6.0.0' } }, + { isNonModelSupported: true, isRelationshipSupported: true }, + ); + + // no v5 api references + expect(componentText).not.toContain('import { API } from "aws-amplify";'); + expect(componentText).not.toContain(`API.graphql`); + + // v6 api + expect(componentText).toContain('import { generateClient } from "aws-amplify/api";'); + expect(componentText).toContain(`const client = generateClient();`); + expect(componentText).toContain('await client.graphql({'); + }); + it('should render a create form for child of 1:m-belongsTo relationship', () => { const { componentText, declaration } = generateWithAmplifyFormRenderer( 'forms/comment-datastore-create', @@ -1249,6 +1360,27 @@ describe('amplify form renderer tests', () => { expect(declaration).toMatchSnapshot(); }); + it('should generate a create form with multiple relationship & cpk - amplify js v6', () => { + const { componentText } = generateWithAmplifyFormRenderer( + 'forms/cpk-teacher-datastore-create', + 'datastore/cpk-relationships', + { ...defaultCLIRenderConfig, ...rendererConfigWithGraphQL, dependencies: { 'aws-amplify': '^6.0.0' } }, + { isNonModelSupported: true, isRelationshipSupported: true }, + ); + + // no datastore reference + expect(componentText).not.toContain('DataStore'); + + // no v5 api references + expect(componentText).not.toContain('import { API } from "aws-amplify";'); + expect(componentText).not.toContain(`API.graphql`); + + // v6 api + expect(componentText).toContain('import { generateClient } from "aws-amplify/api";'); + expect(componentText).toContain(`const client = generateClient();`); + expect(componentText).toContain('await client.graphql({'); + }); + it('should generate an update form with id field instead of belongsTo', () => { const { componentText, declaration } = generateWithAmplifyFormRenderer( 'models/comment-with-postID/forms/CommentUpdateForm', diff --git a/packages/codegen-ui-react/lib/forms/form-renderer-helper/relationship.ts b/packages/codegen-ui-react/lib/forms/form-renderer-helper/relationship.ts index e4ff3287..3c67ad5e 100644 --- a/packages/codegen-ui-react/lib/forms/form-renderer-helper/relationship.ts +++ b/packages/codegen-ui-react/lib/forms/form-renderer-helper/relationship.ts @@ -937,6 +937,7 @@ export const buildManyToManyRelationshipStatements = ( relatedModelPrimaryKeys, joinTableRelatedModelFields, importCollection, + renderConfigDependencies, ) : factory.createCallExpression( factory.createPropertyAccessExpression(