From 3123538456e72c915acb071ade5bd6b332a5106c Mon Sep 17 00:00:00 2001 From: Alan Poulain Date: Tue, 5 Jul 2022 11:17:36 +0200 Subject: [PATCH 1/4] ci: build next app --- .github/workflows/ci.yml | 1 + package.json | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c129b659..636be08b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,4 +24,5 @@ jobs: - run: yarn lint - run: yarn test-gen - run: yarn test-gen-env + - run: yarn test-app - run: yarn check diff --git a/package.json b/package.json index 0a7db16d..8fe92ce6 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,8 @@ "test-gen": "rm -rf ./tmp && yarn build && ./lib/index.js https://demo.api-platform.com ./tmp/react -g react && ./lib/index.js https://demo.api-platform.com ./tmp/react-native -g react-native && ./lib/index.js https://demo.api-platform.com ./tmp/next -g next && ./lib/index.js https://demo.api-platform.com ./tmp/vue -g vue", "test-gen-custom": "rm -rf ./tmp && yarn build && babel src/generators/ReactGenerator.js src/generators/BaseGenerator.js -d ./tmp/gens && cp -r ./templates/react ./templates/react-common ./templates/entrypoint.js ./tmp/gens && ./lib/index.js https://demo.api-platform.com ./tmp/react-custom -g \"$(pwd)/tmp/gens/ReactGenerator.js\" -t ./tmp/gens", "test-gen-swagger": "rm -rf ./tmp && yarn build && ./lib/index.js https://demo.api-platform.com/docs.json ./tmp/react -f swagger && ./lib/index.js https://demo.api-platform.com/docs.json ./tmp/react-native -g react-native -f swagger && ./lib/index.js https://demo.api-platform.com/docs.json ./tmp/vue -g vue -f swagger", - "test-gen-env": "rm -rf ./tmp && yarn build && API_PLATFORM_CLIENT_GENERATOR_ENTRYPOINT=https://demo.api-platform.com API_PLATFORM_CLIENT_GENERATOR_OUTPUT=./tmp ./lib/index.js" + "test-gen-env": "rm -rf ./tmp && yarn build && API_PLATFORM_CLIENT_GENERATOR_ENTRYPOINT=https://demo.api-platform.com API_PLATFORM_CLIENT_GENERATOR_OUTPUT=./tmp ./lib/index.js", + "test-app": "rm -rf ./tmp/app && mkdir -p ./tmp/app && yarn create next-app --typescript ./tmp/app/next && yarn --cwd ./tmp/app/next add lodash.get lodash.has isomorphic-unfetch formik react-query && yarn --cwd ./tmp/app/next add -D @types/lodash && cp -R ./tmp/next/* ./tmp/app/next && rm ./tmp/app/next/pages/index.tsx && rm -rf ./tmp/app/next/pages/api && yarn --cwd ./tmp/app/next build" }, "husky": { "hooks": { From c5d134a49f571ff9eff244b1487b8bec622e9f72 Mon Sep 17 00:00:00 2001 From: mlahfaoui Date: Mon, 14 Mar 2022 14:08:34 +0100 Subject: [PATCH 2/4] feat: adding react query to fetch data --- src/generators/NextGenerator.js | 6 ++ src/generators/NextGenerator.test.js | 2 + templates/next/components/common/Layout.tsx | 16 ++++ .../next/components/common/Pagination.tsx | 2 +- templates/next/components/foo/Form.tsx | 90 ++++++++++++------- templates/next/components/foo/List.tsx | 22 +++-- templates/next/components/foo/Show.tsx | 18 +++- templates/next/pages/_app.tsx | 11 +++ templates/next/pages/foos/[id]/edit.tsx | 31 ++++--- templates/next/pages/foos/[id]/index.tsx | 39 ++++---- templates/next/pages/foos/create.tsx | 3 +- templates/next/pages/foos/index.tsx | 29 +++--- templates/next/types/collection.ts | 16 ++-- templates/next/types/foo.ts | 4 +- templates/next/types/item.ts | 6 +- templates/next/utils/dataAccess.ts | 40 ++++++--- templates/next/utils/mercure.ts | 20 +++-- 17 files changed, 244 insertions(+), 111 deletions(-) create mode 100644 templates/next/components/common/Layout.tsx create mode 100644 templates/next/pages/_app.tsx diff --git a/src/generators/NextGenerator.js b/src/generators/NextGenerator.js index d0893521..0cc8716e 100644 --- a/src/generators/NextGenerator.js +++ b/src/generators/NextGenerator.js @@ -10,6 +10,7 @@ export default class NextGenerator extends BaseGenerator { this.routeAddedtoServer = false; this.registerTemplates(`next/`, [ // components + "components/common/Layout.tsx", "components/common/Pagination.tsx", "components/common/ReferenceLinks.tsx", "components/foo/List.tsx", @@ -26,6 +27,7 @@ export default class NextGenerator extends BaseGenerator { "pages/foos/[id]/edit.tsx", "pages/foos/index.tsx", "pages/foos/create.tsx", + "pages/_app.tsx", // utils "utils/dataAccess.ts", @@ -97,6 +99,7 @@ export default class NextGenerator extends BaseGenerator { // copy with regular name [ // components + "components/common/Layout.tsx", "components/common/Pagination.tsx", "components/common/ReferenceLinks.tsx", @@ -104,6 +107,9 @@ export default class NextGenerator extends BaseGenerator { "types/collection.ts", "types/item.ts", + // pages + "pages/_app.tsx", + // utils "utils/dataAccess.ts", "utils/mercure.ts", diff --git a/src/generators/NextGenerator.test.js b/src/generators/NextGenerator.test.js index d5cb08f3..5208efce 100644 --- a/src/generators/NextGenerator.test.js +++ b/src/generators/NextGenerator.test.js @@ -43,6 +43,7 @@ describe("generate", () => { "/components/abc/List.tsx", "/components/abc/Show.tsx", "/components/abc/Form.tsx", + "/components/common/Layout.tsx", "/components/common/ReferenceLinks.tsx", "/components/common/Pagination.tsx", "/types/Abc.ts", @@ -52,6 +53,7 @@ describe("generate", () => { "/pages/abcs/[id]/edit.tsx", "/pages/abcs/index.tsx", "/pages/abcs/create.tsx", + "/pages/_app.tsx", "/utils/dataAccess.ts", "/utils/mercure.ts", ].forEach((file) => expect(fs.existsSync(tmpobj.name + file)).toBe(true)); diff --git a/templates/next/components/common/Layout.tsx b/templates/next/components/common/Layout.tsx new file mode 100644 index 00000000..a2d41bb9 --- /dev/null +++ b/templates/next/components/common/Layout.tsx @@ -0,0 +1,16 @@ +import { ReactNode, useState } from "react"; +import { DehydratedState, Hydrate, QueryClient, QueryClientProvider } from "react-query"; + +const Layout = ({ children, dehydratedState }: { children: ReactNode, dehydratedState: DehydratedState }) => { + const [queryClient] = useState(() => new QueryClient()); + + return ( + + + {children} + + + ); +} + +export default Layout; diff --git a/templates/next/components/common/Pagination.tsx b/templates/next/components/common/Pagination.tsx index dd316809..a0b3ce24 100644 --- a/templates/next/components/common/Pagination.tsx +++ b/templates/next/components/common/Pagination.tsx @@ -7,7 +7,7 @@ interface Props { const Pagination = ({ collection }: Props) => { const view = collection && collection['{{{hydraPrefix}}}view']; - if (!view) return; + if (!view) return null; const { '{{{hydraPrefix}}}first': first, diff --git a/templates/next/components/foo/Form.tsx b/templates/next/components/foo/Form.tsx index 6625ac11..c4272efb 100644 --- a/templates/next/components/foo/Form.tsx +++ b/templates/next/components/foo/Form.tsx @@ -2,59 +2,89 @@ import { FunctionComponent, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/router"; import { ErrorMessage, Formik } from "formik"; -import { fetch } from "../../utils/dataAccess"; +import { useMutation } from "react-query"; + +import { fetch, FetchError, FetchResponse } from "../../utils/dataAccess"; import { {{{ucf}}} } from '../../types/{{{ucf}}}'; interface Props { {{{lc}}}?: {{{ucf}}}; } +interface SaveParams { + values: {{{ucf}}}; +} + +interface DeleteParams { + id: string; +} + +const save{{{ucf}}} = async ({ values }: SaveParams) => + await fetch<{{ucf}}>(!values["@id"] ? "/{{{name}}}" : values["@id"], { + method: !values["@id"] ? "POST" : "PUT", + body: JSON.stringify(values), + }); + +const delete{{{ucf}}} = async (id: string) => await fetch<{{ucf}}>(id, { method: "DELETE" }); + export const Form: FunctionComponent = ({ {{{lc}}} }) => { - const [error, setError] = useState(null); + const [, setError] = useState(null); const router = useRouter(); - const handleDelete = async () => { - if (!window.confirm("Are you sure you want to delete this item?")) return; + const saveMutation = useMutation | undefined, Error|FetchError, SaveParams>((saveParams) => save{{{ucf}}}(saveParams)); - try { - await fetch({{{lc}}}['@id'], { method: "DELETE" }); + const deleteMutation = useMutation | undefined, Error|FetchError, DeleteParams>(({ id }) => delete{{{ucf}}}(id), { + onSuccess: () => { router.push("/{{{name}}}"); - } catch (error) { + }, + onError: (error)=> { setError(`Error when deleting the resource: ${error}`); console.error(error); } + }); + + const handleDelete = () => { + if (!{{lc}} || !{{lc}}["@id"]) return; + if (!window.confirm("Are you sure you want to delete this item?")) return; + deleteMutation.mutate({ id: {{lc}}["@id"] }); }; - + return ( -
+

{ {{{lc}}} ? `Edit {{{ucf}}} ${ {{~lc}}['@id']}` : `Create {{{ucf}}}` }

{ + validate={() => { const errors = {}; // add your validation logic here return errors; }} - onSubmit={async (values, { setSubmitting, setStatus, setErrors }) => { + onSubmit={(values, { setSubmitting, setStatus, setErrors }) => { const isCreation = !values["@id"]; - try { - await fetch(isCreation ? "/{{{name}}}" : values["@id"], { - method: isCreation ? "POST" : "PUT", - body: JSON.stringify(values), - }); - setStatus({ - isValid: true, - msg: `Element ${isCreation ? 'created': 'updated'}.`, - }); - router.push("/{{{name}}}"); - } catch (error) { - setStatus({ - isValid: false, - msg: `${error.defaultErrorMsg}`, - }); - setErrors(error.fields); - } - setSubmitting(false); + saveMutation.mutate( + { values }, + { + onSuccess: () => { + setStatus({ + isValid: true, + msg: `Element ${isCreation ? "created" : "updated"}.`, + }); + router.push("/{{{name}}}"); + }, + onError: (error) => { + setStatus({ + isValid: false, + msg: `${error.message}`, + }); + if ("fields" in error) { + setErrors(error.fields); + } + }, + onSettled: ()=> { + setSubmitting(false); + } + } + ); }} > {({ @@ -85,7 +115,7 @@ export const Form: FunctionComponent = ({ {{{lc}}} }) => { placeholder="{{{description}}}" {{#if required}}required={true}{{/if}} className={`form-control${errors.{{name}} && touched.{{name}} ? ' is-invalid' : ''}`} - aria-invalid={errors.{{name}} && touched.{{name~}} ? 'true' : null} + aria-invalid={errors.{{name}} && touched.{{name~}} ? 'true' : undefined} onChange={handleChange} onBlur={handleBlur} /> diff --git a/templates/next/components/foo/List.tsx b/templates/next/components/foo/List.tsx index 00b9c9e5..197a6b15 100644 --- a/templates/next/components/foo/List.tsx +++ b/templates/next/components/foo/List.tsx @@ -1,6 +1,7 @@ import { FunctionComponent } from "react"; import Link from "next/link"; -import ReferenceLinks from "../../components/common/ReferenceLinks"; + +import ReferenceLinks from "../common/ReferenceLinks"; import { {{{ucf}}} } from '../../types/{{{ucf}}}'; interface Props { @@ -17,28 +18,37 @@ export const List: FunctionComponent = ({ {{{name}}} }) => ( id -{{#each fields}} + {{#each fields}} {{name}} -{{/each}} + {{/each}} { {{{name}}} && ({{{name}}}.length !== 0) && {{{name}}}.map( ( {{{lc}}} ) => ( + {{{lc}}}['@id'] && {{#each fields}} - {{#if reference}}{{else}}{ {{{../lc}}}['{{{name}}}'] }{{/if}} + + {{#if reference}} + + {{else if (compare type "==" "Date") }} + { {{{../lc}}}['{{{name}}}']?.toLocaleString() } + {{else}} + { {{{../lc}}}['{{{name}}}'] } + {{/if}} + {{/each}} - +