diff --git a/dashboard/README.md b/dashboard/README.md
index 965a122..5e8cfab 100644
--- a/dashboard/README.md
+++ b/dashboard/README.md
@@ -1,5 +1,3 @@
-This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
-
## Getting Started
First, run the development server:
@@ -14,25 +12,15 @@ pnpm dev
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
-You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
-
-[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
-
-The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
-
-This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
-
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
-
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
-
-## Deploy on Vercel
-
-The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
-
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
+- [@reduxjs/toolkit](https://redux-toolkit.js.org/) - The official, opinionated, batteries-included toolset for efficient Redux development
+- [axios](https://axios-http.com/docs/intro) - Promise based HTTP client for the browser and node.js
+- [formik](https://formik.org/) - Build forms in React, without the tears
+- [yup](https://www.npmjs.com/package/yup) - Yup is a schema builder for runtime value parsing and validation.
+- [tailwindcss](https://tailwindcss.com/) - Rapidly build modern websites without ever leaving your HTML.
+- [jwt-decode](jwt-decode)
diff --git a/dashboard/src/app/layout.tsx b/dashboard/src/app/layout.tsx
index c086ad9..79c32ed 100644
--- a/dashboard/src/app/layout.tsx
+++ b/dashboard/src/app/layout.tsx
@@ -1,12 +1,12 @@
-import "@/styles/globals.css";
-import { Metadata } from "next";
+import Providers from '@/lib/Providers';
+import '@/styles/globals.css';
+import { Metadata } from 'next';
export const metadata: Metadata = {
- title: "ES Dashboard",
- description: "English Sphere Dashboard",
- authors: [{ name: "Fahim Montasir", url: "https://moontasir.web.app/" }],
- keywords:
- "ES Dashboard, English Sphere Dashboard, English Sphere content center",
+ title: 'ES Dashboard',
+ description: 'English Sphere Dashboard',
+ authors: [{ name: 'Fahim Montasir', url: 'https://moontasir.web.app/' }],
+ keywords: 'ES Dashboard, English Sphere Dashboard, English Sphere content center',
};
export default function RootLayout({
@@ -18,7 +18,9 @@ export default function RootLayout({
}) {
return (
-
{children}
+
+ {children}
+
);
}
diff --git a/dashboard/src/lib/Providers.tsx b/dashboard/src/lib/Providers.tsx
new file mode 100644
index 0000000..8cc5435
--- /dev/null
+++ b/dashboard/src/lib/Providers.tsx
@@ -0,0 +1,9 @@
+'use client';
+import { store } from '@/redux/store';
+import { Provider } from 'react-redux';
+
+const Providers = ({ children }: { children: React.ReactNode }) => {
+ return {children};
+};
+
+export default Providers;
diff --git a/dashboard/src/redux/api/authApi.ts b/dashboard/src/redux/api/authApi.ts
new file mode 100644
index 0000000..d5de7ff
--- /dev/null
+++ b/dashboard/src/redux/api/authApi.ts
@@ -0,0 +1,18 @@
+import { tagTypes } from '../tag-types';
+import { baseApi } from './baseApi';
+const AUTH_URL = '/auth';
+
+export const authApi = baseApi.injectEndpoints({
+ endpoints: build => ({
+ userLogin: build.mutation({
+ query: loginData => ({
+ url: `${AUTH_URL}/login`,
+ method: 'POST',
+ data: loginData,
+ }),
+ invalidatesTags: [tagTypes.user],
+ }),
+ }),
+});
+
+export const { useUserLoginMutation } = authApi;
diff --git a/dashboard/src/redux/api/baseApi.ts b/dashboard/src/redux/api/baseApi.ts
new file mode 100644
index 0000000..bd148d1
--- /dev/null
+++ b/dashboard/src/redux/api/baseApi.ts
@@ -0,0 +1,12 @@
+import { axiosBaseQuery } from '@/helpers/axios/axiosBaseQuery';
+import { getBaseUrl } from '@/helpers/config/envConfig';
+import { createApi } from '@reduxjs/toolkit/query/react';
+import { tagTypesList } from '../tag-types';
+
+// Define a service using a base URL and expected endpoints
+export const baseApi = createApi({
+ reducerPath: 'api',
+ baseQuery: axiosBaseQuery({ baseUrl: getBaseUrl() }),
+ endpoints: () => ({}),
+ tagTypes: tagTypesList,
+});
diff --git a/dashboard/src/redux/api/courseApi.ts b/dashboard/src/redux/api/courseApi.ts
new file mode 100644
index 0000000..bc705ea
--- /dev/null
+++ b/dashboard/src/redux/api/courseApi.ts
@@ -0,0 +1,69 @@
+import { ICourse, IMeta } from '@/types';
+import { baseApi } from './baseApi';
+import { tagTypes } from '../tag-types';
+
+const COURSE_URL = '/courses';
+
+export const courseApi = baseApi.injectEndpoints({
+ endpoints: build => ({
+ // get all
+ courses: build.query({
+ query: (arg: Record) => {
+ return {
+ url: COURSE_URL,
+ method: 'GET',
+ params: arg,
+ };
+ },
+ transformResponse: (response: ICourse[], meta: IMeta) => {
+ return {
+ courses: response,
+ meta,
+ };
+ },
+ providesTags: [tagTypes.course],
+ }),
+ // get single
+ course: build.query({
+ query: (id: string) => ({
+ url: `${COURSE_URL}/${id}`,
+ method: 'GET',
+ }),
+ providesTags: [tagTypes.course],
+ }),
+ // create
+ addCourse: build.mutation({
+ query: data => ({
+ url: COURSE_URL,
+ method: 'POST',
+ data,
+ }),
+ invalidatesTags: [tagTypes.course],
+ }),
+ // update
+ updateCourse: build.mutation({
+ query: data => ({
+ url: `${COURSE_URL}/${data.id}`,
+ method: 'PATCH',
+ data: data.body,
+ }),
+ invalidatesTags: [tagTypes.course],
+ }),
+ // delete
+ deleteCourse: build.mutation({
+ query: id => ({
+ url: `${COURSE_URL}/${id}`,
+ method: 'DELETE',
+ }),
+ invalidatesTags: [tagTypes.course],
+ }),
+ }),
+});
+
+export const {
+ useCoursesQuery,
+ useCourseQuery,
+ useAddCourseMutation,
+ useDeleteCourseMutation,
+ useUpdateCourseMutation,
+} = courseApi;
diff --git a/dashboard/src/redux/hooks.ts b/dashboard/src/redux/hooks.ts
new file mode 100644
index 0000000..0f1e227
--- /dev/null
+++ b/dashboard/src/redux/hooks.ts
@@ -0,0 +1,28 @@
+import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
+import type { RootState, AppDispatch } from "./store";
+import { useEffect, useState } from "react";
+
+// Use throughout your app instead of plain `useDispatch` and `useSelector`
+export const useAppDispatch: () => AppDispatch = useDispatch;
+export const useAppSelector: TypedUseSelectorHook = useSelector;
+
+interface IDebounced {
+ searchQuery: string;
+ delay: number;
+}
+
+export const useDebounced = ({ searchQuery, delay }: IDebounced) => {
+ const [debouncedValue, setDebouncedValue] = useState(searchQuery);
+
+ useEffect(() => {
+ const handler = setTimeout(() => {
+ setDebouncedValue(searchQuery);
+ }, delay);
+
+ return () => {
+ clearTimeout(handler);
+ };
+ }, [searchQuery, delay]);
+
+ return debouncedValue;
+};
diff --git a/dashboard/src/redux/rootReducer.ts b/dashboard/src/redux/rootReducer.ts
new file mode 100644
index 0000000..5934473
--- /dev/null
+++ b/dashboard/src/redux/rootReducer.ts
@@ -0,0 +1,6 @@
+import { baseApi } from "./api/baseApi";
+
+export const reducer = {
+ [baseApi.reducerPath]: baseApi.reducer,
+}
+
diff --git a/dashboard/src/redux/store.ts b/dashboard/src/redux/store.ts
new file mode 100644
index 0000000..a000d94
--- /dev/null
+++ b/dashboard/src/redux/store.ts
@@ -0,0 +1,16 @@
+import { baseApi } from './api/baseApi';
+import { reducer } from './rootReducer';
+import { configureStore } from '@reduxjs/toolkit'
+
+export const store = configureStore({
+ reducer,
+ middleware: (getDefaultMiddleware) =>
+ getDefaultMiddleware().concat(baseApi.middleware),
+})
+
+
+
+// Infer the `RootState` and `AppDispatch` types from the store itself
+export type RootState = ReturnType
+// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
+export type AppDispatch = typeof store.dispatch
\ No newline at end of file
diff --git a/dashboard/src/redux/tag-types.ts b/dashboard/src/redux/tag-types.ts
new file mode 100644
index 0000000..de4079f
--- /dev/null
+++ b/dashboard/src/redux/tag-types.ts
@@ -0,0 +1,6 @@
+export enum tagTypes {
+ course = 'course',
+ user = 'user',
+}
+
+export const tagTypesList = [tagTypes.course, tagTypes.user];
diff --git a/dashboard/src/schemas/login.ts b/dashboard/src/schemas/login.ts
new file mode 100644
index 0000000..5623148
--- /dev/null
+++ b/dashboard/src/schemas/login.ts
@@ -0,0 +1,6 @@
+import * as yup from "yup";
+
+export const loginSchema = yup.object().shape({
+ id: yup.string().required("UserId is required"),
+ password: yup.string().min(6).max(32).required(),
+});