@efanworks/tasks-with-redux 1.0.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +23 -0
- package/src/components/AddTask/index.tsx +24 -0
- package/src/components/TaskItem/index.tsx +36 -0
- package/src/components/TaskList/index.tsx +21 -0
- package/src/components/Tasks/index.tsx +16 -0
- package/src/index.ts +1 -0
- package/src/store/index.ts +13 -0
- package/src/store/tasksSlice.ts +76 -0
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@efanworks/tasks-with-redux",
|
|
3
|
+
"version": "1.0.1-alpha.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"author": "",
|
|
10
|
+
"license": "ISC",
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"registry": "https://registry.npmjs.org/",
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@efanworks/tasks-api": "^1.0.1-alpha.0",
|
|
17
|
+
"@reduxjs/toolkit": "^2.12.0",
|
|
18
|
+
"react-redux": "^9.3.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@efanworks/tasks-types": "^1.0.1-alpha.1"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import {useAppDispatch } from '../../store';
|
|
3
|
+
import { addTask } from "../../store/tasksSlice";
|
|
4
|
+
|
|
5
|
+
export const AddTask = () => {
|
|
6
|
+
const [newTaskText, setNewTaskText] = useState("");
|
|
7
|
+
const dispatch = useAppDispatch();
|
|
8
|
+
|
|
9
|
+
const handleAddTask = async () => {
|
|
10
|
+
await dispatch(addTask(newTaskText));
|
|
11
|
+
setNewTaskText("");
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<>
|
|
16
|
+
<input
|
|
17
|
+
type="text"
|
|
18
|
+
value={newTaskText}
|
|
19
|
+
onChange={(e) => setNewTaskText(e.target.value)}
|
|
20
|
+
/>
|
|
21
|
+
<button onClick={handleAddTask}>add</button>
|
|
22
|
+
</>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Task } from "@efanworks/tasks-types";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { changeTask, deleteTask } from "../../store/tasksSlice";
|
|
4
|
+
import { useAppDispatch } from "../../store";
|
|
5
|
+
|
|
6
|
+
export const TaskItem = ({ task }: { task: Task }) => {
|
|
7
|
+
const [editing, setEditing] = useState(false);
|
|
8
|
+
const [value, setValue] = useState(task.text);
|
|
9
|
+
const dispatch = useAppDispatch();
|
|
10
|
+
|
|
11
|
+
const handleSave = async () => {
|
|
12
|
+
await dispatch(changeTask({ id: task.id, text: value }));
|
|
13
|
+
setEditing(false);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<li>
|
|
18
|
+
{editing ? (
|
|
19
|
+
<>
|
|
20
|
+
<input
|
|
21
|
+
type="text"
|
|
22
|
+
value={value}
|
|
23
|
+
onChange={(e) => setValue(e.target.value)}
|
|
24
|
+
/>
|
|
25
|
+
<button onClick={handleSave}>save</button>
|
|
26
|
+
</>
|
|
27
|
+
) : (
|
|
28
|
+
<>
|
|
29
|
+
{task.text} - {task.done ? "done" : "undo"}
|
|
30
|
+
<button onClick={() => setEditing(true)}>edit</button>
|
|
31
|
+
</>
|
|
32
|
+
)}
|
|
33
|
+
<button onClick={() => deleteTask(task.id)}>delete</button>
|
|
34
|
+
</li>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { useAppDispatch, useAppSelector } from "../../store";
|
|
3
|
+
import { TaskItem } from "../TaskItem";
|
|
4
|
+
import { fetchTasks } from "../../store/tasksSlice";
|
|
5
|
+
|
|
6
|
+
export const TaskList = () => {
|
|
7
|
+
const tasks = useAppSelector((state) => state.tasks.tasks);
|
|
8
|
+
const dispatch = useAppDispatch();
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
dispatch(fetchTasks());
|
|
12
|
+
}, []);
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<ul>
|
|
16
|
+
{tasks.map((task) => (
|
|
17
|
+
<TaskItem key={task.id} task={task} />
|
|
18
|
+
))}
|
|
19
|
+
</ul>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ErrorBoundary } from "react-error-boundary";
|
|
2
|
+
import { Provider } from "react-redux";
|
|
3
|
+
import { store } from "../../store";
|
|
4
|
+
import { AddTask } from "../AddTask";
|
|
5
|
+
import { TaskList } from "../TaskList";
|
|
6
|
+
|
|
7
|
+
export const Tasks = () => {
|
|
8
|
+
return (
|
|
9
|
+
<ErrorBoundary fallbackRender={() => <div>Page Not Found</div>}>
|
|
10
|
+
<Provider store={store}>
|
|
11
|
+
<AddTask />
|
|
12
|
+
<TaskList />
|
|
13
|
+
</Provider>
|
|
14
|
+
</ErrorBoundary>
|
|
15
|
+
);
|
|
16
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Tasks } from "./components/Tasks";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { configureStore } from "@reduxjs/toolkit";
|
|
2
|
+
import { useDispatch, useSelector } from "react-redux";
|
|
3
|
+
import tasksRuducer from "./tasksSlice";
|
|
4
|
+
|
|
5
|
+
export const store = configureStore({
|
|
6
|
+
reducer: { tasks: tasksRuducer },
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export type RootState = ReturnType<typeof store.getState>;
|
|
10
|
+
export type AppDispatch = typeof store.dispatch;
|
|
11
|
+
|
|
12
|
+
export const useAppSelector = useSelector.withTypes<RootState>();
|
|
13
|
+
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
|
|
2
|
+
import type { Task } from "@efanworks/tasks-types";
|
|
3
|
+
import {
|
|
4
|
+
fetchTasks as fetchTasksAPI,
|
|
5
|
+
addTask as addTaskAPI,
|
|
6
|
+
changeTask as changeTaskAPI,
|
|
7
|
+
deleteTask as deleteTaskAPI,
|
|
8
|
+
} from "@efanworks/tasks-api";
|
|
9
|
+
|
|
10
|
+
const initialState: {
|
|
11
|
+
tasks: Task[];
|
|
12
|
+
} = {
|
|
13
|
+
tasks: [],
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const fetchTasks = createAsyncThunk(
|
|
17
|
+
"tasks/fetchTasks",
|
|
18
|
+
async () => await fetchTasksAPI(),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export const addTask = createAsyncThunk(
|
|
22
|
+
"tasks/addTask",
|
|
23
|
+
async (text: string) => {
|
|
24
|
+
return await addTaskAPI(text);
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export const changeTask = createAsyncThunk(
|
|
29
|
+
"tasks/changeTask",
|
|
30
|
+
async ({ id, done, text }: { id: string; done?: boolean; text?: string }) => {
|
|
31
|
+
const updates = Object.assign(
|
|
32
|
+
{},
|
|
33
|
+
text ? { text } : {},
|
|
34
|
+
done ? { done } : {},
|
|
35
|
+
);
|
|
36
|
+
return await changeTaskAPI(id, updates);
|
|
37
|
+
},
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export const deleteTask = createAsyncThunk(
|
|
41
|
+
"tasks/deleteTask",
|
|
42
|
+
async (id: string) => {
|
|
43
|
+
await deleteTaskAPI(id);
|
|
44
|
+
return id;
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const tasksSlice = createSlice({
|
|
49
|
+
name: "tasks",
|
|
50
|
+
initialState,
|
|
51
|
+
reducers: {},
|
|
52
|
+
extraReducers: (build) => {
|
|
53
|
+
build
|
|
54
|
+
.addCase(fetchTasks.fulfilled, (state, action) => {
|
|
55
|
+
state.tasks = action.payload;
|
|
56
|
+
})
|
|
57
|
+
.addCase(addTask.fulfilled, (state, action) => {
|
|
58
|
+
state.tasks.push(action.payload);
|
|
59
|
+
})
|
|
60
|
+
.addCase(changeTask.fulfilled, (state, action) => {
|
|
61
|
+
state.tasks = state.tasks.map((t) => {
|
|
62
|
+
if (t.id === action.payload.id) {
|
|
63
|
+
return {
|
|
64
|
+
...action.payload,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return t;
|
|
68
|
+
});
|
|
69
|
+
})
|
|
70
|
+
.addCase(deleteTask.fulfilled, (state, action) => {
|
|
71
|
+
state.tasks = state.tasks.filter((t) => t.id !== action.payload);
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
export default tasksSlice.reducer;
|