Customer Relation Manager - Front end

React, React-table, Redux, Redux-toolkit, TailwindCSS, Vite, React-router, Eslint, Prettier

Description

In this project i use TailwindCSS, because need more customization in the UI, is fast and also very flexible.

  • Includes Login page for Auth, React-big-calendar- React-table
  • Redux-toolkit for state management.

Key takeaways

There are multiple purposes of this project is can connect this front end crm with an e-commerce, backend, apis, services

Store

Combines all reducers and apply async-await fuction in replace of Thunks.

View code
1export const storeSlice = createSlice({
2  name: "storeData",
3  initialState: {
4    // Pages
5    actualPage: "dashboard",
6
7    // Modals
8    isProductModalOpen: false,
9    isModalActionOpen: false,
10    isNeWProduct: false,
11    isModalFeatureOpen: false,
12
13    // Tables
14    infoData: [],
15    infoColumns: [],
16    selectedRecord: [],
17    showSelectedrow: false,
18    dataList: [],
19  },
20  reducers: {
21    // Pages
22    onChangeView: (state, payload) => {},
23
24    // Modals
25    onOpenProductModal: (state, { payload }) => {
26      state.isNeWProduct
27        ? (state.isProductModalOpen = true)
28        : (state.isProductModalOpen = true);
29      // state.selectedRecord = [{title: '',category: '',description: '',image:'',price:''}]
30    },
31    onCloseProductModal: (state) => {
32      state.isProductModalOpen = false;
33      state.selectedRecord = [];
34    },
35
36    onOpenDeleteModal: (state) => {
37      state.isModalActionOpen = true;
38    },
39
40    onCloseDeleteModal: (state) => {
41      state.isModalActionOpen = false;
42    },
43
44    onOpenFeatureModal: (state) => {
45      state.isModalFeatureOpen = true;
46    },
47
48    onCloseFeatureModal: (state) => {
49      state.isModalFeatureOpen = false;
50    },
51
52    // Tables
53    onLoadTable: (state, { payload }) => {
54      switch (payload.page) {
55        case "dashboard":
56          state.actualPage = payload.page;
57          break;
58
59        case "customers":
60          state.actualPage = payload.page;
61          state.dataList = payload.data;
62          state.infoData = payload.data;
63          state.infoColumns = Object.keys(payload.data[0])
64            .filter(
65              (key) =>
66                // Exceptions
67                key !== "name" && key !== "address" && key !== "__v"
68            )
69            .map((key) => {
70              return { Header: key, accessor: key };
71            });
72          break;
73
74        case "orders":
75          state.actualPage = payload.page;
76          state.dataList = payload.data;
77          state.infoData = payload.data;
78          state.infoColumns = Object.keys(payload.data[0])
79            .filter(
80              (key) =>
81                // Exceptions
82                key !== "products" && key !== "__v"
83            )
84            .map((key) => {
85              return { Header: key, accessor: key };
86            });
87
88          break;
89
90        case "products":
91          state.actualPage = payload.page;
92          state.dataList = payload.data;
93          state.infoData = payload.data;
94          state.infoColumns = Object.keys(payload.data[0])
95            .filter(
96              (key) => key !== "rating" /* Exceptions  && key !== "price" */
97            )
98            .map((key) => {
99              return { Header: key, accessor: key };
100            });
101
102          break;
103
104        default:
105          break;
106      }
107    },
108
109    onSelectRecord: (state, { payload }) => {
110      state.selectedRecord = payload;
111      console.log(state.selectedRecord);
112    },
113
114    // CRUD Table
115    onAddNewProduct: (state, { payload }) => {
116      state.infoData.push(payload);
117      state.selectedRecord = [];
118      state.isProductModalOpen = false;
119    },
120    onUpdateProduct: (state, { payload }) => {
121      state.infoData = state.infoData.map((product) => {
122        if (product.id === payload.id) {
123          return payload;
124        }
125        return product;
126      });
127      state.isProductModalOpen = false;
128    },
129
130    onDeleteProduct: (state, { payload }) => {
131      state.infoData = state.infoData.filter(
132        (product) => product.id !== state.selectedRecord.id
133      );
134      state.selectedRecord = null;
135      state.isModalActionOpen = false;
136    },
137
138    // Sidebar
139
140    onClickSidebar: (state, { payload }) => {
141      state.actualPage = payload;
142    },
143  }, // Reducers End
144});
145
146// Action creators are generated for each case reducer function
147export const {
148  // Pages
149  onChangeView,
150
151  // Modals
152  onOpenProductModal,
153  onCloseProductModal,
154  onOpenFeatureModal,
155
156  onOpenDeleteModal,
157  onCloseDeleteModal,
158
159  // Table
160  onLoadTable,
161  onSelectRecord,
162
163  // CRUD Table
164  onAddNewProduct,
165  onUpdateProduct,
166  onDeleteProduct,
167  onCloseFeatureModal,
168
169  // Sidebar
170  onClickSidebar,
171} = storeSlice.actions;
172

Components

This is an SPA aplication, then i use a logic of container, and reused the same container following the dry principle, if you check the code, you can see that i try to reused all components, also the table is the same for all the views, the modal is the same, only change the custom form for each modal-view.

View code
1export const Dashboard = () => {
2  const { actualPage } = useSelector((state) => state.storeData);
3
4  const showFabAddNew = () => {
5    switch (actualPage) {
6      case "dashboard":
7        break;
8      case "customers":
9        return <FabAddNew />;
10      case "products":
11        return <FabAddNew />;
12      case "orders":
13        return <FabAddNew />;
14
15      default:
16        break;
17    }
18  };
19
20  const showContainer = () => {
21    switch (actualPage) {
22      case "dashboard":
23        return <DashboardPage />;
24      case "customers":
25        return <CustomersPage />;
26      case "products":
27        return <ProductsPage />;
28      case "orders":
29        return <OrdersPage />;
30
31      default:
32        break;
33    }
34  };
35
36  return (
37    <>
38      <Sidebar />
39      <div className="ml-auto  mb-6 lg:w-[75%] xl:w-[80%] 2xl:w-[85%]">
40        <Navbar />
41        <div className="p-2 pt-2 2xl:container">
42          <div className="flex flex-col  border-gray-300 rounded-xl">
43            {showContainer()}
44          </div>
45        </div>
46      </div>
47      <CrudTableModal />
48      {showFabAddNew()}
49      <ModalConfirmation />
50      <FeatureModal />
51    </>
52  );
53};
54

Screenshots

Dashboard view, estructure used in crm apps.
1. Dashboard view, estructure used in crm apps.
react table very flexible for this purpose.
2. react table very flexible for this purpose.
Again a modal, but this is not a package i build this modal.
3. Again a modal, but this is not a package i build this modal.