Skip to content

Commit 585ad49

Browse files
Next generator CRUD (#256)
* next template CRUD * added redirection after delete, create and edit element
1 parent 7c13dd2 commit 585ad49

File tree

13 files changed

+236
-57
lines changed

13 files changed

+236
-57
lines changed

src/generators/NextGenerator.js

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,21 @@ export default class NextGenerator extends BaseGenerator {
1010
// components
1111
"components/common/ReferenceLinks.tsx",
1212
"components/foo/List.tsx",
13-
"components/foo/ListItem.tsx",
1413
"components/foo/Show.tsx",
14+
"components/foo/Form.tsx",
1515

1616
// interfaces
1717
"error/SubmissionError.ts",
1818

19-
// interfaces
20-
"interfaces/Collection.ts",
21-
"interfaces/foo.ts",
19+
// types
20+
"types/Collection.ts",
21+
"types/foo.ts",
2222

2323
// pages
24-
"pages/foos/[id].tsx",
24+
"pages/foos/[id]/index.tsx",
25+
"pages/foos/[id]/edit.tsx",
2526
"pages/foos/index.tsx",
27+
"pages/foos/create.tsx",
2628

2729
// utils
2830
"utils/dataAccess.ts",
@@ -59,33 +61,33 @@ export default class NextGenerator extends BaseGenerator {
5961
`${dir}/components/common`,
6062
`${dir}/config`,
6163
`${dir}/error`,
62-
`${dir}/interfaces`,
64+
`${dir}/types`,
6365
`${dir}/pages`,
66+
`${dir}/pages/${context.lc}s/[id]`,
6467
`${dir}/utils`,
6568
].forEach((dir) => this.createDir(dir, false));
6669

6770
// copy with patterned name
6871
this.createDir(`${dir}/components/${context.lc}`);
6972
this.createDir(`${dir}/pages/${context.lc}s`);
73+
this.createDir(`${dir}/pages/${context.lc}s/[id]`);
7074
[
7175
// components
7276
"components/%s/List.tsx",
73-
"components/%s/ListItem.tsx",
7477
"components/%s/Show.tsx",
78+
"components/%s/Form.tsx",
7579

7680
// pages
77-
"pages/%ss/[id].tsx",
81+
"pages/%ss/[id]/index.tsx",
82+
"pages/%ss/[id]/edit.tsx",
7883
"pages/%ss/index.tsx",
84+
"pages/%ss/create.tsx",
7985
].forEach((pattern) =>
8086
this.createFileFromPattern(pattern, dir, context.lc, context)
8187
);
8288

8389
// interface pattern should be camel cased
84-
this.createFile(
85-
"interfaces/foo.ts",
86-
`${dir}/interfaces/${context.ucf}.ts`,
87-
context
88-
);
90+
this.createFile("types/foo.ts", `${dir}/types/${context.ucf}.ts`, context);
8991

9092
// copy with regular name
9193
[
@@ -95,8 +97,8 @@ export default class NextGenerator extends BaseGenerator {
9597
// error
9698
"error/SubmissionError.ts",
9799

98-
// interfaces
99-
"interfaces/Collection.ts",
100+
// types
101+
"types/Collection.ts",
100102

101103
// utils
102104
"utils/dataAccess.ts",

src/generators/NextGenerator.test.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,24 @@ describe("generate", () => {
4141
[
4242
"/config/entrypoint.ts",
4343
"/components/abc/List.tsx",
44-
"/components/abc/ListItem.tsx",
4544
"/components/abc/Show.tsx",
45+
"/components/abc/Form.tsx",
4646
"/components/common/ReferenceLinks.tsx",
4747
"/error/SubmissionError.ts",
48-
"/interfaces/Abc.ts",
49-
"/interfaces/Collection.ts",
50-
"/pages/abcs/[id].tsx",
48+
"/types/Abc.ts",
49+
"/types/Collection.ts",
50+
"/pages/abcs/[id]/index.tsx",
51+
"/pages/abcs/[id]/edit.tsx",
5152
"/pages/abcs/index.tsx",
53+
"/pages/abcs/create.tsx",
5254
"/utils/dataAccess.ts",
5355
].forEach((file) => expect(fs.existsSync(tmpobj.name + file)).toBe(true));
5456

5557
[
5658
"/components/abc/List.tsx",
57-
"/components/abc/ListItem.tsx",
5859
"/components/abc/Show.tsx",
59-
"/interfaces/Abc.ts",
60+
"/components/abc/Form.tsx",
61+
"/types/Abc.ts",
6062
].forEach((file) => {
6163
expect(fs.existsSync(tmpobj.name + file)).toBe(true);
6264
expect(fs.readFileSync(tmpobj.name + file, "utf8")).toMatch(/bar/);
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { FunctionComponent, useState } from "react";
2+
import { Formik } from "formik";
3+
import { {{{ucf}}} } from '../../types/{{{ucf}}}';
4+
import Link from "next/link";
5+
import { useRouter } from "next/router";
6+
7+
interface Props {
8+
{{{lc}}}?: {{{ucf}}};
9+
}
10+
11+
export const Form: FunctionComponent<Props> = ({ {{{lc}}} }) => {
12+
const [error, setError] = useState(null);
13+
const router = useRouter();
14+
15+
const handleDelete = () => {
16+
if (window.confirm("Are you sure you want to delete this item?")) {
17+
try {
18+
fetch({ {{{lc}}}['@id'] }, { method: "DELETE" });
19+
router.push("/{{{name}}}");
20+
} catch (error) {
21+
setError("Error when deleting the resource.");
22+
console.error(error);
23+
}
24+
}
25+
};
26+
27+
return (
28+
<div>
29+
{ {{{lc}}} ? <h1>Edit {{{lc}}}['@id']</h1> : <h1>Create</h1>}
30+
<Formik
31+
initialValues={ {{{lc}}} ?? new{{{lc}}}() }
32+
validate={(values) => {
33+
const errors = {};
34+
//set your validation logic here
35+
return errors;
36+
}}
37+
onSubmit={(values, { setSubmitting, setStatus }) => {
38+
const isCreation = !{{{lc}}}["@id"];
39+
try {
40+
fetch(isCreation ? "/{{{name}}}" : {{{lc}}}["@id"],
41+
{
42+
method: isCreation ? "POST" : "PATCH",
43+
body: JSON.stringify(values),
44+
}
45+
);
46+
setStatus({
47+
isValid: true,
48+
msg: `Element ${isCreation ? 'created': 'updated'}.`,
49+
});
50+
router.push("/{{{name}}}");
51+
} catch (error) {
52+
setStatus({
53+
isValid: false,
54+
msg: `Error when ${isCreation ? 'creating': 'updating'} the resource.`,
55+
});
56+
}
57+
setSubmitting(false);
58+
}}
59+
>
60+
{({
61+
values,
62+
status,
63+
handleChange,
64+
handleBlur,
65+
handleSubmit,
66+
isSubmitting,
67+
}) => (
68+
<form onSubmit={handleSubmit}>
69+
{{#each fields}}
70+
<div className='form-group'>
71+
<label>{{name}}</label>
72+
<input
73+
className='form-control'
74+
type='text'
75+
name='isbn'
76+
onChange={handleChange}
77+
onBlur={handleBlur}
78+
value={ values.{{name}} }
79+
required
80+
/>
81+
</div>
82+
{/* {errors.{{name}} && touched.{{name}} && errors.{{name}} */}
83+
{{/each}}
84+
{status && status.msg && (
85+
<div
86+
className={`alert ${
87+
status.isValid ? "alert-success" : "alert-danger"
88+
}`}
89+
role='alert'
90+
>
91+
{status.msg}
92+
</div>
93+
)}
94+
95+
{error && (
96+
<div className='alert alert-danger' role='alert'>
97+
{error}
98+
</div>
99+
)}
100+
101+
<button
102+
type='submit'
103+
className='btn btn-success'
104+
disabled={isSubmitting}
105+
>
106+
Submit
107+
</button>
108+
</form>
109+
)}
110+
</Formik>
111+
<Link href="/{{{name}}}">
112+
<a className='btn btn-primary'>Back to list</a>
113+
</Link>
114+
{ {{{lc}}} && (
115+
<button className='btn btn-danger' onClick={handleDelete}>
116+
<a>Delete</a>
117+
</button>
118+
)}
119+
</div>
120+
);
121+
};

templates/next/components/foo/List.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { FunctionComponent } from 'react';
2-
import { ListItem } from './ListItem';
3-
import { {{{ucf}}} } from '../../interfaces/{{{ucf}}}';
2+
import { {{{ucf}}} } from '../../types/{{{ucf}}}';
3+
import Link from "next/link";
44

55
interface Props {
66
{{{name}}}: {{{ucf}}}[];
@@ -9,6 +9,9 @@ interface Props {
99
export const List: FunctionComponent<Props> = ({ {{{name}}} }) => (
1010
<div>
1111
<h1>{{{ucf}}} List</h1>
12+
<Link href="/{{{name}}}/create">
13+
<a className="btn btn-primary">Create</a>
14+
</Link>
1215
<table className="table table-responsive table-striped table-hover">
1316
<thead>
1417
<tr>
@@ -20,8 +23,14 @@ export const List: FunctionComponent<Props> = ({ {{{name}}} }) => (
2023
</tr>
2124
</thead>
2225
<tbody>
23-
{ {{{name}}} && ({{{name}}}.length !== 0) && {{{name}}}.map({{{lc}}} => (
24-
<ListItem key={ {{{lc}}}['@id'] } {{{lc}}}={ {{{lc}}} } />
26+
{ {{{name}}} && ({{{name}}}.length !== 0) && {{{name}}}.map( ( {{{lc}}} ) => (
27+
<tr key={ {{{lc}}}['@id'] }>
28+
<th scope="row"><ReferenceLinks items={ {{{lc}}}['@id'] } type="{{{lc}}}" /></th>
29+
{{#each fields}}
30+
<td>{{#if reference}}<ReferenceLinks items={ {{{../lc}}}['{{{name}}}'] } type="{{{reference.title}}}" />{{else}}{ {{{../lc}}}['{{{name}}}'] }{{/if}}</td>
31+
{{/each}}
32+
<td><ReferenceLinks items={ {{{lc}}}['@id'] } type="{{{lc}}}" useIcon={true} /></td>
33+
</tr>
2534
))}
2635
</tbody>
2736
</table>

templates/next/components/foo/ListItem.tsx

Lines changed: 0 additions & 17 deletions
This file was deleted.

templates/next/components/foo/Show.tsx

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,29 @@
1-
import { FunctionComponent } from 'react';
1+
import { FunctionComponent, useState } from 'react';
22
import Link from 'next/link';
33
import { ReferenceLinks } from '../common/ReferenceLinks';
4-
import { {{{ucf}}} } from '../../interfaces/{{{ucf}}}';
4+
import { {{{ucf}}} } from '../../types/{{{ucf}}}';
5+
import { useRouter } from "next/router";
56

67
interface Props {
78
{{{lc}}}: {{{ucf}}};
89
}
910

10-
export const Show: FunctionComponent<Props> = ({ {{{lc}}} }) => (
11+
export const Show: FunctionComponent<Props> = ({ {{{lc}}} }) => {
12+
const [error, setError] = useState(null);
13+
const router = useRouter();
14+
15+
const handleDelete = () => {
16+
if (window.confirm("Are you sure you want to delete this item?")) {
17+
try {
18+
fetch({ {{{lc}}}["@id"] }, { method: "DELETE" });
19+
router.push("/{{{name}}}");
20+
} catch (error) {
21+
setError("Error when deleting the resource.");
22+
console.error(error);
23+
}
24+
}
25+
};
26+
return(
1127
<div>
1228
<h1>Show { {{{lc}}}['@id'] }</h1>
1329
<table className="table table-responsive table-striped table-hover">
@@ -26,8 +42,19 @@ export const Show: FunctionComponent<Props> = ({ {{{lc}}} }) => (
2642
{{/each}}
2743
</tbody>
2844
</table>
45+
{error && (
46+
<div className='alert alert-danger' role='alert'>
47+
{error}
48+
</div>
49+
)}
2950
<Link href="/{{{name}}}"><a className="btn btn-primary">
3051
Back to list
3152
</a></Link>
53+
<Link href="/{{{name}}}/edit"><a className="btn btn-warning">
54+
Edit
55+
</a></Link>
56+
<button className='btn btn-danger' onClick={handleDelete}>
57+
<a>Delete</a>
58+
</button>
3259
</div>
33-
);
60+
)};

templates/next/interfaces/foo.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { NextComponentType, NextPageContext } from 'next';
2+
import { Form } from '../../../components/{{{lc}}}/Form';
3+
import { {{{ucf}}} } from '../../types/{{{ucf}}}';
4+
import { fetch } from '../../../utils/dataAccess';
5+
6+
interface Props {
7+
{{{lc}}}: {{{ucf}}};
8+
};
9+
10+
const Page: NextComponentType<NextPageContext, Props, Props> = ({ {{{lc}}} }) => {
11+
12+
return (
13+
<Form {{{lc}}}={ {{{lc}}} }/>
14+
);
15+
};
16+
17+
Page.getInitialProps = async ({ asPath }: NextPageContext) => {
18+
const {{{lc}}} = await fetch(asPath.replace( '/edit', ''));
19+
20+
return { {{{lc}}} };
21+
};
22+
23+
export default Page;

templates/next/pages/foos/[id].tsx renamed to templates/next/pages/foos/[id]/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { NextComponentType, NextPageContext } from 'next';
22
import { Show } from '../../components/{{{lc}}}/Show';
3-
import { {{{ucf}}} } from '../../interfaces/{{{ucf}}}';
4-
import { fetch } from '../../utils/dataAccess';
3+
import { {{{ucf}}} } from '../../types/{{{ucf}}}';
4+
import { fetch } from '../../../utils/dataAccess';
55

66
interface Props {
77
{{{lc}}}: {{{ucf}}};

templates/next/pages/foos/create.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { NextComponentType, NextPageContext } from "next";
2+
import { Form } from "../../components/{{{lc}}}/Form";
3+
4+
const Page: NextComponentType<NextPageContext> = () => <Form />;
5+
6+
export default Page;

0 commit comments

Comments
 (0)