Skip to content

Commit ed4eafd

Browse files
treetipsusername
and
username
authored
Add todo sample application (#1)
* feat: add material-table * feat: ignore .idea directory * refactor: refactor structure * refactor: deleted because of automatic generation. * feat: add todo sample application * docs: add createSlice and createAsyncThunk and createEntityAdapter. Co-authored-by: username <[email protected]>
1 parent 9e279ae commit ed4eafd

31 files changed

+866
-90
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ node_modules
3535
# Optional REPL history
3636
.node_repl_history
3737

38+
# Intellij IDEA
39+
.idea
3840

3941
## Application
4042
.next

README.md

+10
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22

33
This is a sample for `server-side rendering` using `TypeScript` , `Next.js` , `Redux Toolkit` , and `Material-UI` .
44

5+
I also used the latest features such as `createSlice` , `createAsyncThunk` , and `createEntityAdapter` .
6+
57
`VSCode` , `prettier` and `TSLint` provide real-time formatting, syntax checking and organizing of unused imports.
68

79
これは、 `TypeScript` , `Next.js` , `Redux Toolkit` , `Material-UI` を使った `サーバーサイドレンダリング` に対応したサンプルです。
810

11+
`createSlice``createAsyncThunk``createEntityAdapter` といった最新機能も使ってみました。
12+
913
`VSCode``prettier``TSLint` によって、リアルタイムに整形と構文チェックと未使用 import の整理が行われます。
1014

1115
## Live demo
@@ -18,8 +22,14 @@ This is a sample for `server-side rendering` using `TypeScript` , `Next.js` , `R
1822
- [Typescript](https://www.typescriptlang.org/)
1923
- [Next.js](https://nextjs.org/)
2024
- [Material-UI](https://material-ui.com/)
25+
- [material-table](https://material-table.com/#/)
2126
- [Redux](https://redux.js.org/)
2227
- [Redux Toolkit](https://redux-toolkit.js.org/)
28+
- [createSlice](https://redux-toolkit.js.org/api/createSlice)
29+
- [createAsyncThunk](https://redux-toolkit.js.org/api/createAsyncThunk)
30+
- [createEntityAdapter](https://redux-toolkit.js.org/api/createEntityAdapter)
31+
- [createSelector](https://redux-toolkit.js.org/api/createSelector)
32+
- It using most of the major features of the redux toolkit !!
2333
- [TSLint](https://palantir.github.io/tslint/)
2434

2535
## Requirement

components/molecules/TodoList.tsx

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import MaterialTable from "material-table"
2+
import React, { useEffect } from "react"
3+
import { useTodo } from "../../hooks"
4+
5+
type Props = {}
6+
7+
/**
8+
* TODO list
9+
* @param props Props
10+
*/
11+
export const TodoList = function (props: Props) {
12+
const {} = props
13+
const {
14+
isFetching,
15+
fetchAllTodos,
16+
addTodo,
17+
editTodo,
18+
deleteTodo,
19+
todos,
20+
} = useTodo()
21+
22+
useEffect(() => {
23+
fetchAllTodos()
24+
}, [])
25+
26+
return (
27+
<MaterialTable
28+
title="TODO List"
29+
columns={[
30+
{ title: "id", field: "id", type: "numeric", editable: "never" },
31+
{ title: "name", field: "name" },
32+
{ title: "complete", field: "complete", type: "boolean" },
33+
{
34+
title: "create time",
35+
field: "createdAt",
36+
type: "datetime",
37+
editable: "never",
38+
},
39+
{
40+
title: "update time",
41+
field: "updatedAt",
42+
type: "datetime",
43+
editable: "never",
44+
},
45+
]}
46+
options={{
47+
actionsColumnIndex: 99,
48+
search: true,
49+
}}
50+
data={todos}
51+
isLoading={isFetching}
52+
editable={{
53+
onRowAdd: (newData) =>
54+
new Promise((resolve, reject) => {
55+
addTodo({
56+
todo: newData,
57+
})
58+
.then(() => resolve(todos))
59+
.catch((e) => reject(e))
60+
}),
61+
onRowUpdate: (newData, _) =>
62+
new Promise((resolve, reject) => {
63+
editTodo({
64+
todo: newData,
65+
})
66+
.then((payload) => resolve(payload))
67+
.catch((e) => reject(e))
68+
}),
69+
onRowDelete: (oldData) =>
70+
new Promise((resolve, reject) => {
71+
deleteTodo({
72+
id: oldData.id,
73+
})
74+
.then(() => resolve(todos))
75+
.catch((e) => reject(e))
76+
}),
77+
}}
78+
/>
79+
)
80+
}

components/molecules/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from "./NextListItem"
22
export * from "./PageHeader"
3+
export * from "./TodoList"

constants/Page.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Color } from "@material-ui/core"
2-
import { blue, pink, red } from "@material-ui/core/colors"
2+
import { blue, pink, red, yellow } from "@material-ui/core/colors"
33
import { SvgIconProps } from "@material-ui/core/SvgIcon"
44
import { Home, Info, Save } from "@material-ui/icons"
55
import { IEnum } from "."
@@ -34,6 +34,16 @@ export class Page implements IEnum<Page> {
3434
Save,
3535
blue
3636
)
37+
public static readonly TODO = new Page(
38+
3,
39+
"TODO",
40+
"TODO sample",
41+
"TODO sample | sample",
42+
"The TODO sample application using createAsyncThunk and createEntityAdapter.",
43+
"/todo",
44+
Save,
45+
yellow
46+
)
3747
public static readonly ERROR = new Page(
3848
99,
3949
"Error",

hooks/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from "./useCounter"
22
export * from "./usePage"
3+
export * from "./useTodo"

hooks/useTodo.ts

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { unwrapResult } from "@reduxjs/toolkit"
2+
import { useCallback } from "react"
3+
import { useDispatch, useSelector } from "react-redux"
4+
import { Todo } from "../model"
5+
import {
6+
addTodoAction,
7+
deleteTodoAction,
8+
editTodoAction,
9+
fetchAllTodosAction,
10+
fetchTodoAction,
11+
} from "../store/todo/action"
12+
import {
13+
allTodoSelector,
14+
isFetchingSelector,
15+
todoSelector,
16+
} from "../store/todo/selector"
17+
18+
/**
19+
* TODO custom hook
20+
*/
21+
export const useTodo = () => {
22+
const dispatch = useDispatch()
23+
const isFetching = useSelector(isFetchingSelector)
24+
const todo = useSelector(todoSelector)
25+
const todos = useSelector(allTodoSelector)?.map((t) => ({
26+
id: t.id,
27+
name: t.name,
28+
complete: t.complete,
29+
createdAt: t.createdAt,
30+
updatedAt: t.updatedAt,
31+
}))
32+
33+
const fetchAllTodos = useCallback(
34+
(arg?: { offset?: number; limit?: number }) => {
35+
return dispatch(
36+
fetchAllTodosAction({
37+
offset: arg?.offset || 0,
38+
limit: arg?.limit || 5,
39+
})
40+
).then(unwrapResult)
41+
},
42+
[dispatch]
43+
)
44+
45+
const fetchTodo = useCallback(
46+
(arg: { id: number }) => {
47+
return dispatch(fetchTodoAction(arg)).then(unwrapResult)
48+
},
49+
[dispatch]
50+
)
51+
52+
const addTodo = useCallback(
53+
(arg: { todo: Todo }) => {
54+
return dispatch(addTodoAction(arg)).then(unwrapResult)
55+
},
56+
[dispatch]
57+
)
58+
59+
const editTodo = useCallback(
60+
(arg: { todo: Todo }) => {
61+
return dispatch(editTodoAction(arg)).then(unwrapResult)
62+
},
63+
[dispatch]
64+
)
65+
66+
const deleteTodo = useCallback(
67+
(arg: { id: number }) => {
68+
return dispatch(deleteTodoAction(arg)).then(unwrapResult)
69+
},
70+
[dispatch]
71+
)
72+
73+
return {
74+
isFetching,
75+
todos,
76+
todo,
77+
fetchAllTodos,
78+
fetchTodo,
79+
addTodo,
80+
editTodo,
81+
deleteTodo,
82+
} as const
83+
}

model/ApiErrorResponse.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* Api error response
3+
*/
4+
export type ApiErrorResponse = {
5+
statusCode: number
6+
message: string
7+
error?: Error
8+
}

model/TestData.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Todo } from "./Todo"
2+
3+
// test data
4+
export let testTodos: Todo[] = []
5+
6+
for (let i = 0; i < 6; i++) {
7+
testTodos.push({
8+
id: i + 1,
9+
name: `Task ${i + 1}`,
10+
complete: i % 2 == 0,
11+
createdAt: new Date(),
12+
updatedAt: new Date(),
13+
})
14+
}

model/Todo.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* TODO model
3+
*/
4+
export type Todo = {
5+
id: number
6+
name: string
7+
complete: boolean
8+
createdAt: Date
9+
updatedAt: Date
10+
}

model/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./Todo"

0 commit comments

Comments
 (0)