diff --git a/package.json b/package.json
index 07b580b..ae92a60 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,8 @@
"@types/react-router-dom": "^5.1.7",
"@types/styled-components": "^5.1.10",
"@types/yup": "^0.29.11",
+ "ag-grid-community": "30.0.6",
+ "ag-grid-react": "30.0.6",
"formik": "^2.2.9",
"react": "^17.0.2",
"react-dom": "^17.0.2",
diff --git a/public/index.html b/public/index.html
index e6cc647..9b05d89 100644
--- a/public/index.html
+++ b/public/index.html
@@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
-
Customer List
+ Customer Management
diff --git a/src/App.tsx b/src/App.tsx
index 9b36496..28f0e67 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -2,6 +2,7 @@ import * as React from "react";
import { StyledMain, StyledHeader, StyledHeaderText } from "./StyledApp";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Home from "./views/Home";
+import { AddCustomerForm } from "./components/AddCustomer/AddCustomerForm";
const App: React.FC = () => {
return (
@@ -11,9 +12,12 @@ const App: React.FC = () => {
-
+
+
+
+
diff --git a/src/components/AddCustomer/AddCustomerForm.tsx b/src/components/AddCustomer/AddCustomerForm.tsx
index 52ebf9e..1878f71 100644
--- a/src/components/AddCustomer/AddCustomerForm.tsx
+++ b/src/components/AddCustomer/AddCustomerForm.tsx
@@ -1,25 +1,72 @@
import { Field, Formik, FormikHelpers } from "formik";
import * as React from "react";
-import { ICustomer, Customer } from "../../types/types";
+import { ICustomer, Customer, CustomerState } from "../../types/types";
import {
StyledForm,
StyledInput,
StyledLabel,
StyledAddButton,
+ StyledCancelButton,
+ StyledError,
} from "./StyledAddCustomerForm";
+import { addCustomer } from "../../redux/actions/customerActions";
+import { Dispatch } from "redux";
+import { shallowEqual, useDispatch, useSelector } from "react-redux";
+import { useHistory } from "react-router-dom";
+import * as Yup from "yup";
-type Props = {
- saveCustomer: (customer: ICustomer | any) => void;
-};
+const CustomerSchema = Yup.object().shape({
+ firstName: Yup.string()
+ .min(2, "Too Short!")
+ .max(50, "Too Long!")
+ .required("Required"),
+ lastName: Yup.string()
+ .min(2, "Too Short!")
+ .max(50, "Too Long!")
+ .required("Required"),
+ phoneNumber: Yup.string()
+ .matches(/^(\(0[1-9]\)|0[1-9])?( ?-?[0-9]){10,10}$/, "Invalid phone number")
+ .required("Required"),
+ birthday: Yup.date().required("Required"),
+});
+
+export const AddCustomerForm: React.FC = () => {
+ let history = useHistory();
+ const dispatch: Dispatch = useDispatch();
+
+ const customers: ICustomer[] = useSelector(
+ (state: CustomerState) => state.customers,
+ shallowEqual
+ );
+
+ const saveCustomer = React.useCallback(
+ (customer: ICustomer | any) => {
+ let isCustomerFound = customers.find(cust => cust.firstName === customer.firstName && cust.lastName === customer.lastName && cust.phoneNumber === customer.phoneNumber)
+ if(!isCustomerFound) {
+ dispatch(addCustomer(customer));
+ history.push("/");
+ } else {
+ alert('Customer already exist')
+ }
+
+
+ },
+ [dispatch]
+ );
+
+ const cancel = () => {
+ history.push("/");
+ };
-export const AddCustomerForm: React.FC = ({ saveCustomer }) => {
return (
@@ -28,34 +75,76 @@ export const AddCustomerForm: React.FC = ({ saveCustomer }) => {
setSubmitting(false);
}}
>
-
- First Name
-
-
- Last Name
-
-
- Phone Number
-
-
- Add Customer
-
+ {({ errors, touched }) => (
+
+
+ First Name*{" "}
+ {errors.firstName && touched.firstName ? (
+ {errors.firstName}
+ ) : null}
+
+
+
+
+ Last Name*{" "}
+ {errors.lastName && touched.lastName ? (
+ {errors.lastName}
+ ) : null}
+
+
+
+
+ Phone Number*{" "}
+ {errors.phoneNumber && touched.phoneNumber ? (
+ {errors.phoneNumber}
+ ) : null}
+
+
+
+
+
+ Birthday*{" "}
+ {errors.birthday && touched.birthday ? (
+ {errors.birthday}
+ ) : null}
+
+
+
+
+ Add Customer
+ Cancel
+
+ )}
);
};
diff --git a/src/components/AddCustomer/StyledAddCustomerForm.ts b/src/components/AddCustomer/StyledAddCustomerForm.ts
index a7a4bb9..fd33141 100644
--- a/src/components/AddCustomer/StyledAddCustomerForm.ts
+++ b/src/components/AddCustomer/StyledAddCustomerForm.ts
@@ -30,3 +30,20 @@ export const StyledAddButton = styled.button`
border-radius: 4px;
font-size: 16px;
`;
+
+export const StyledCancelButton = styled.button`
+ padding: 1rem;
+ background-color: gray;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ font-size: 16px;
+ cursor: pointer;
+ margin-top: 5px
+`;
+
+export const StyledError = styled.div`
+ float: right;
+ font-size: 14px;
+ color: red;
+`;
diff --git a/src/components/Customer/Customer.tsx b/src/components/Customer/Customer.tsx
index 8fd093a..f8e84d7 100644
--- a/src/components/Customer/Customer.tsx
+++ b/src/components/Customer/Customer.tsx
@@ -30,6 +30,9 @@ export const Customer: React.FC = ({ customer, removeCustomer }) => {
Phone number: {customer.phoneNumber}
+
+ Birthday: {customer.birthday}
+
deleteCustomer(customer)}>
Delete
diff --git a/src/components/Customer/StyledCustomer.ts b/src/components/Customer/StyledCustomer.ts
index f6e8885..079073a 100644
--- a/src/components/Customer/StyledCustomer.ts
+++ b/src/components/Customer/StyledCustomer.ts
@@ -19,7 +19,8 @@ export const StyledCustomerInfo = styled.p`
`;
export const StyledCustomerDelete = styled.button`
- padding: 1rem;
+ padding: 6px;
+ height: 30px;
background-color: #dc1616;
color: white;
border: none;
diff --git a/src/components/Grid/ButtonCellRenderer.tsx b/src/components/Grid/ButtonCellRenderer.tsx
new file mode 100644
index 0000000..d297772
--- /dev/null
+++ b/src/components/Grid/ButtonCellRenderer.tsx
@@ -0,0 +1,21 @@
+import React from "react";
+import { StyledCustomerDelete } from "../Customer/StyledCustomer";
+import { Dispatch } from "redux";
+import { useDispatch } from "react-redux";
+import { ICustomer } from "../../types/types";
+import { removeCustomer } from "../../redux/actions/customerActions";
+
+export const ButtonCellRenderer: React.FC = (params) => {
+ const dispatch: Dispatch = useDispatch();
+
+ const deleteCustomer = React.useCallback(
+ (customer: ICustomer) => dispatch(removeCustomer(customer)),
+ [dispatch, removeCustomer]
+ );
+
+ return (
+ deleteCustomer(params.data)}>
+ Delete
+
+ );
+};
diff --git a/src/redux/reducers/customerReducers.tsx b/src/redux/reducers/customerReducers.tsx
index d6d837e..d0c93f9 100644
--- a/src/redux/reducers/customerReducers.tsx
+++ b/src/redux/reducers/customerReducers.tsx
@@ -8,18 +8,21 @@ export const initialState: CustomerState = {
firstName: "Charles",
lastName: "Babbage",
phoneNumber: "0412 123 123",
+ birthday: "1992-10-05",
},
{
id: 2,
firstName: "Alan",
lastName: "Turing",
phoneNumber: "(03) 9599 1234",
+ birthday: "1990-11-02",
},
{
id: 3,
firstName: "Ada",
lastName: "Lovelace",
phoneNumber: "+61 423 345 567",
+ birthday: "1984-04-09",
},
],
};
@@ -35,6 +38,7 @@ export const customerReducer = (
firstName: action.customer.firstName,
lastName: action.customer.lastName,
phoneNumber: action.customer.phoneNumber,
+ birthday: action.customer.birthday,
};
return {
...state,
diff --git a/src/types/types.ts b/src/types/types.ts
index 83e2415..3c4cde2 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -2,6 +2,7 @@ export interface Customer {
firstName: string;
lastName: string;
phoneNumber: string;
+ birthday: string;
}
export interface ICustomer extends Customer {
diff --git a/src/views/Home.tsx b/src/views/Home.tsx
index 8911b39..8d1d1f3 100644
--- a/src/views/Home.tsx
+++ b/src/views/Home.tsx
@@ -1,34 +1,72 @@
import * as React from "react";
-import { useSelector, shallowEqual, useDispatch } from "react-redux";
-import { Customer } from "../components/Customer/Customer";
-import { AddCustomerForm } from "../components/AddCustomer/AddCustomerForm";
-import { Dispatch } from "redux";
+import { useSelector, shallowEqual } from "react-redux";
import { CustomerState, ICustomer } from "../types/types";
-import { addCustomer, removeCustomer } from "../redux/actions/customerActions";
+import styled from "styled-components";
+import { Link } from "react-router-dom";
+import { AgGridReact } from "ag-grid-react";
+import { ButtonCellRenderer } from "../components/Grid/ButtonCellRenderer"
+import "ag-grid-community/styles/ag-grid.css";
+import "ag-grid-community/styles/ag-theme-alpine.css";
+import { ColDef } from "ag-grid-community";
+
+const StyledAddCustomerButton = styled(Link)`
+ padding: 1rem;
+ background-color: rgb(4, 121, 205);
+ color: white;
+ border: none;
+ border-radius: 4px;
+ font-size: 16px;
+ width: 100%;
+ text-align: center;
+ text-decoration: auto;
+ margin-bottom: 5px;
+`;
+
+const filterConfig = {
+ filter: true,
+ floatingFilter: true,
+};
const Home: React.FC = () => {
- const customers: readonly ICustomer[] = useSelector(
+ const gridRef = React.useRef>(null);
+
+ const customers: ICustomer[] = useSelector(
(state: CustomerState) => state.customers,
shallowEqual
);
- const dispatch: Dispatch = useDispatch();
+ const colDefs: ColDef[] = [
+ { field: "firstName", ...filterConfig, sortable: true },
+ { field: "lastName", ...filterConfig, sortable: true },
+ { field: "phoneNumber", ...filterConfig, sortable: true },
+ { field: "birthday", ...filterConfig, sortable: true },
+ { field: "", cellRenderer: ButtonCellRenderer}
+ ];
- const saveCustomer = React.useCallback(
- (customer: ICustomer) => dispatch(addCustomer(customer)),
- [dispatch]
- );
+ const onFirstDataRendered = React.useCallback((params) => {
+ gridRef?.current?.api.sizeColumnsToFit();
+ }, []);
return (
<>
-
- {customers.map((customer: ICustomer) => (
-
- ))}
+ Customer List
+
+ New Customer
+
+
+
+ ref={gridRef}
+ rowData={customers}
+ columnDefs={colDefs}
+ onFirstDataRendered={onFirstDataRendered}
+ >
+
>
);
};