Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
470 changes: 242 additions & 228 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"antd": "^4.15.2",
"antd": "^4.24.16",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-redux": "^9.1.1",
Expand Down Expand Up @@ -47,4 +47,4 @@
"public"
]
}
}
}
8 changes: 7 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import "./assets/css/style.css";
import { connect } from "react-redux";
import "../node_modules/antd/dist/antd.css";
import MainComponent from "./components/mainComponent";
import { addUser, getUsers } from "./actions/userActions";
import { addUser, getUsers, deleteUser, updateUser } from "./actions/userActions";
import 'antd/dist/antd.css';


function App(props) {
return (
Expand All @@ -20,6 +22,10 @@ const mapStateToProps = (state) => ({
const mapDispatchToProps = {
getUsers,
addUser,
deleteUser,
updateUser,


};

export default connect(mapStateToProps, mapDispatchToProps)(App);
42 changes: 37 additions & 5 deletions src/actions/userActions.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
export const getUsers = () => async dispatch => {
try{
try {
const response = await fetch('http://example.com/users')
const parsedResponse = await response.json()
dispatch({
type: 'LIST_USERS',
payload: parsedResponse
});
}catch(e){
} catch(e) {
console.log(e);
}
};

export const addUser = (payload) => async dispatch => {
try{
try {
const response = await fetch("http://example.com/user", {
method: "POST",
body: JSON.stringify(payload),
Expand All @@ -21,10 +21,42 @@ export const addUser = (payload) => async dispatch => {
}
});
const parsedResponse = await response.json()
if(parsedResponse.success){
if(parsedResponse.success) {
dispatch(getUsers());
}
}catch(e){
} catch(e) {
console.log(e);
}
};

export const deleteUser = (id) => async dispatch => {
try {
const response = await fetch(`http://example.com/user/${id}`, {
method: "DELETE"
});
const parsedResponse = await response.json();
if(parsedResponse.success) {
dispatch(getUsers());
}
} catch(e) {
console.log(e);
}
};

export const updateUser = (id, payload) => async dispatch => {
try {
const response = await fetch(`http://example.com/user/${id}`, {
method: "PUT",
body: JSON.stringify(payload),
headers: {
"Content-type": "application/json; charset=UTF-8"
}
});
const parsedResponse = await response.json();
if(parsedResponse.success) {
dispatch(getUsers());
}
} catch(e) {
console.log(e);
}
};
55 changes: 55 additions & 0 deletions src/assets/css/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
.table-container {
margin-top: 20px;
}

.user-table {
width: 100%;
border-collapse: collapse;
}

.user-table th,
.user-table td {
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}

.user-table th {
background-color: #f2f2f2;
font-weight: bold;
}

.user-table tr:nth-child(even) {
background-color: #f9f9f9;
}

.header-box {
display: flex;
gap: 10px;
margin-bottom: 20px;
}

.header-box input {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}

.header-box button {
padding: 8px 16px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}

.header-box button:hover {
background-color: #45a049;
}

.error-message {
color: red;
font-size: 14px;
margin-top: 5px;
}
59 changes: 38 additions & 21 deletions src/components/commonInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,51 @@ import React, { useState } from "react";
const InputHandler = ({ onSubmit, editMode = false }) => {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [errors, setErrors] = useState({});

const validateForm = () => {
const newErrors = {};
if (!name.trim()) newErrors.name = "Name is required";
if (!email.trim()) newErrors.email = "Email is required";
else if (!/\S+@\S+\.\S+/.test(email)) newErrors.email = "Email is invalid";
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};

const handleSubmit = (e) => {
e.preventDefault();
if (!name || !email) return;
onSubmit({ name, email });
if (validateForm()) {
onSubmit({ name, email });
setName("");
setEmail("");
}
};

return (
<div className="header-box">
<input
type="text"
placeholder="Name"
onChange={(e) => {
setName(e.target.value);
}}
/>
<input
type="text"
placeholder="Email"
onChange={(e) => {
setEmail(e.target.value);
}}
/>
<button type="primary">
{!!editMode ? "Edit user" : "Add user"}
<form className="header-box" onSubmit={handleSubmit}>
<div>
<input
type="text"
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
{errors.name && <div className="error-message">{errors.name}</div>}
</div>
<div>
<input
type="text"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
{errors.email && <div className="error-message">{errors.email}</div>}
</div>
<button type="submit">
{editMode ? "Edit user" : "Add user"}
</button>
</div>
</form>
);
};

export default InputHandler;
export default InputHandler;
133 changes: 121 additions & 12 deletions src/components/mainComponent.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,132 @@
import React, { useEffect } from "react";
import InputHandler from "./commonInput";
import SimpleTable from "./simpleTable";
import React, { useEffect, useState } from "react";
import { Table, Button, Input, Form, Modal } from 'antd';
import { EditOutlined, DeleteOutlined } from '@ant-design/icons';

function MainComponent(props) {
const { getUsers, userState, addUser } = props;
const { getUsers, userState, addUser, deleteUser, updateUser } = props;
const [form] = Form.useForm();
const [editForm] = Form.useForm();
const [editingUser, setEditingUser] = useState(null);

const handleSubmit = ({ name, email }) => {
addUser({ name, email });
};
useEffect(() => {
getUsers();
}, []);
}, [getUsers]);

const handleSubmit = (values) => {
addUser(values);
form.resetFields();
};

const handleDelete = (id) => {
deleteUser(id);
};

const handleEdit = (record) => {
setEditingUser(record);
editForm.setFieldsValue(record);
};

const handleUpdate = (values) => {
updateUser(editingUser.id, values);
setEditingUser(null);
};

const columns = [
{
title: 'Id',
dataIndex: 'id',
key: 'id',
},
{
title: 'Name',
dataIndex: 'name',
key: 'name',
},
{
title: 'Email',
dataIndex: 'email',
key: 'email',
},
{
title: 'Actions',
key: 'actions',
render: (_, record) => (
<>
<Button
type="primary"
icon={<EditOutlined />}
style={{ marginRight: 8 }}
onClick={() => handleEdit(record)}
>
Edit
</Button>
<Button
danger
icon={<DeleteOutlined />}
onClick={() => handleDelete(record.id)}
>
Delete
</Button>
</>
),
},
];

return (
<div id="main-container-wrapper">
<InputHandler onSubmit={handleSubmit} />
<SimpleTable dataSource={userState.users} />
<div style={{ padding: 24 }}>
<Form form={form} layout="inline" onFinish={handleSubmit} style={{ marginBottom: 16 }}>
<Form.Item
name="name"
rules={[{ required: true, message: 'Please input the name!' }]}
>
<Input placeholder="Name" />
</Form.Item>
<Form.Item
name="email"
rules={[
{ required: true, message: 'Please input the email!' },
{ type: 'email', message: 'Please enter a valid email!' }
]}
>
<Input placeholder="Email" />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Add user
</Button>
</Form.Item>
</Form>

<Table columns={columns} dataSource={userState.users} rowKey="id" />

<Modal
title="Edit User"
visible={!!editingUser}
onOk={() => editForm.submit()}
onCancel={() => setEditingUser(null)}
>
<Form form={editForm} onFinish={handleUpdate} layout="vertical">
<Form.Item
name="name"
label="Name"
rules={[{ required: true, message: 'Please input the name!' }]}
>
<Input />
</Form.Item>
<Form.Item
name="email"
label="Email"
rules={[
{ required: true, message: 'Please input the email!' },
{ type: 'email', message: 'Please enter a valid email!' }
]}
>
<Input />
</Form.Item>
</Form>
</Modal>
</div>
);
}

export default MainComponent;
export default MainComponent;
Loading