npm install xlsx import * as xlsx from 'xlsx';
npx create-react-app 'name'
npm install @mui/material @emotion/react @emotion/styled npm install @fontsource/roboto npm install @mui/icons-material npm install @mui/x-data-grid npm install @mui/x-data-grid-generator npm install @mui/x-date-pickers npm install dayjs --save npm install @mui/lab @mui/material
- index.html
import 'react-toastify/dist/ReactToastify.css';
npm install --save react-toastify
npm install react-router-dom
npm install react-redux npm install @reduxjs/toolkit
npm install redux-persist
npm install axios
npm install json-server
- İlk olaraq src folder-ində 'pages' folderi yaradırıq.
- 'pages' daxilində yaradacağımız səhifələrin folder-lərini və həmin səhifələrin componenti olacaqsa onlarında daxilində 'component' folderi yaradırıq.
- Ortaq layout üçün də 'Layout' folderi yaradırıq.
- 'Layout' daxilində 'SharedLayout.js' faylı yaradırıq və daxilində səhfələri çağırmaq üçün '' componentini çağırırıq.
- SharedLayout.js
import { Outlet } from 'react-router-dom'
const SharedLayout = () => {
return (
<div>
<Outlet/>
</div>
)
}
export default SharedLayout
- Və ən sonunda App.js faylında pages struktunu qoşuruq.
- App.js
<BrowserRouter>
<Routes>
<Route path='/' element={<SharedLayout />}>
<Route index element={<User />} />
<Route path='groups' element={<Group />} />
<Route path='vendors' element={<Vendor />} />
<Route path='requests' element={<Request />} />
<Route path='payments' element={<Payment />} />
</Route>
<Route path='login' element={<Login />} />
<Route path='register' element={<Register />} />
<Route path='*' element={<ErrorPage />} />
</Routes>
</BrowserRouter>
- src folderində store folderi və daxilində index.js faylı yaradırıq. "reduxjs/toolkit" kitabxanasından istifadə edərək "store" yaradırıq və "setupListeners" funksiyasından istifadə edərək dinləyicilərə "store.dispatch" funksiyası təyin edilir. 'store' proqramdakı dataların saxlandığı yerdir və dataların yenilənməsi üçün dispatch funksiyasından istifadə olunur.
- store/index.js
import { configureStore } from "@reduxjs/toolkit";
import { setupListeners } from '@reduxjs/toolkit/dist/query'
export const store = configureStore({
reducer: {},
})
setupListeners(store.dispatch);
- index.js faylında "react-dom/client" kitabxanasındakı "createRoot" funksiyasından istifadə edərək HTML sənədində "root" ID-si olan elementə React tətbiqini təqdim edir. O, həmçinin "react-redux" kitabxanasındakı "Provider" komponentindən istifadə etməklə store tətbiqinin bütün komponentlərinə girişi təmin edilir. "store" və "App" komponentlərindən istifadə edərək tətbiqin başlanğıc nöqtəsini yaradılır.
- index.js
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import { store } from './store'
import App from './App';
const el = document.getElementById('root');
const root = createRoot(el);
root.render(
<Provider store={store}>
<App />
</Provider>
);
- Əvvəlcə proyektdə api-lər token ilə olduğu üçün authentication olunmalıdır. Bunun üçün login sistemi qurmalıyıq. store folderində slice folderi və daxilində 'authSlie' faylı yaradırıq. "@reduxjs/toolkit" kitabxanasından "createSlice" funksiyasından istifadə edərək "auth" slice yaradılır. Bu slice-ın ilkin vəziyyəti token və userIdentifier dəyişənlərinin sıfır olduğu obyektdir. Bu dilimdə iki reducer müəyyən edilmişdir. Birincisi, "setCredentials", action payload dataları götürür və state-dəki token və userIdentifier dəyərlərini yeniləyir. O, həmçinin bu dəyərləri localStorage-də saxlayır. İkinci reducer "logOut" vəziyyətində token və userIdentifier dəyərlərini sıfırlayır, həmçinin localStorage-də token və userIdentifier dəyərlərini silir. Ən sonda, "setCredentials", "logOut" və "authReducer" export edilir.
- authSlice.js
import { createSlice } from "@reduxjs/toolkit";
const authSlice = createSlice({
name: 'auth',
initialState: {
token: null,
userIdentifier: null
},
reducers: {
setCredentials: (state, action) => {
const { data } = action.payload
state.token = data.token.accessToken
state.userIdentifier = data.userIdentifier
localStorage.setItem('token', state.token)
localStorage.setItem('userIdentifier', state.userIdentifier)
},
logOut: (state, action) => {
state.token = null
state.userIdentifier = null
localStorage.removeItem('token')
localStorage.removeItem('userIdentifier')
}
}
})
export const { setCredentials, logOut } = authSlice.actions
export const authReducer = authSlice.reducer
// export const selectCurrentToken = (state) => state.auth.token
- Təkrarlanacaq kodlar üçüm src folderində 'config.js' faylı yaradırıq. "baseUrl" dəyişəni "prepareHeaders" funksiyasını təyin edilir. Bu funksiya localStorage-dən "token" və "userIdentifier" dəyərlərini götürür və "token" dəyəri varsa, dəyərləri "authorization" və "authToken" key-ləri ilə birlikdə headers obyektinə əlavə edir. Ən sonda, headers obyektini qaytarır.
- config.js
export const baseUrl = "http://localhost:7252/api/";
export const prepareHeaders = (headers) => {
const token = localStorage.getItem("token");
const userIdentifier = localStorage.getItem("userIdentifier");
if (token) {
headers.set('authorization', `Bearer ${token}`);
headers.set('authToken', `${userIdentifier}`);
}
return headers;
}
- Hər api-də təkrarlanacaq kod bloku üçün bir helper folderi yaradırıq. Həmin təkrarlanacaq kod bloku baseQuery olduğundan 'createBaseQuery' faylı yaradırıq. "@reduxjs/toolkit/dist/query/react" kitabxanasından "fetchBaseQuery" funksiyasını və "config" faylından "baseUrl" və "prepareHeaders" funksiyaları import edilir. Sonra "createBaseQuery" funksiyasını təyin edirik. Bu funksiya "baseUrl" və "prepareHeaders" funksiyalarından istifadə edərək "fetchBaseQuery" funksiyasını çağırır və qaytarır. Bu, sorğuların ediləcəyi URL və başlıqlar üçün əsas sorğu yaradılır.
- createBaseQuery
import { fetchBaseQuery } from "@reduxjs/toolkit/dist/query/react";
import { baseUrl, prepareHeaders } from "../../config";
export const createBaseQuery = () => {
return fetchBaseQuery({
baseUrl,
prepareHeaders
})
}
- slice folderində 'tokenSlice.js' faylı yaradırıq.Bu fayl token idarəetmə modulu yaratmaq üçün istifadə olunur. Birincisi, "baseUrl" və "prepareHeaders" kimi konfiqurasiya dəyişənləri üçün "config.js" faylından daxil edir. Sonra o, "fetchBaseQuery" funksiyasından istifadə edərək "baseQuery" yaradır və həmin "baseQuery" üzərində işləyən "createApi" funksiyası ilə API yaradır. Bu API 403 status kodu qəbul edərsə, o, yeni giriş nişanı əldə etmək üçün yeniləmə nişanı göndərir və sonra yeni giriş nişanı ilə orijinal sorğunu yenidən sınayır. Yeniləmə nişanı da etibarsızdırsa, istifadəçi sistemdən çıxır.
- token.js
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/dist/query/react";
import { logOut, setCredentials } from "./authSlice";
import { baseUrl, prepareHeaders } from "../../config";
const baseQuery = fetchBaseQuery({
baseUrl,
prepareHeaders
})
const baseQueryWithReauth = async (args, api, extraOptions) => {
let result = await baseQuery(args, api, extraOptions);
if (result?.error?.originalStatus === 403) {
// send refresh token to get new access token
const refreshResult = await baseQuery('/refresh', api, extraOptions);
if (refreshResult?.data) {
const user = result.data?.data.token.accessToken
localStorage.setItem('token', result.data?.data.token.accessToken)
// store the new token
api.dispatch(setCredentials({ ...refreshResult.data, user }))
// retry the original query with new access token
result = await baseQuery(args, api, extraOptions)
} else {
api.dispatch(logOut())
}
}
return result
}
export const tokenSlice = createApi({
baseQuery: baseQueryWithReauth,
endpoints: builder => ({})
})
- api folderində 'authApi.js' faylı yaradırıq. "authApi" yaratmaq üçün "tokenSlice"-dən bir neçə 'endpoints'-i əhatə edir. Xüsusilə, "login" endpoints istifadəçinin daxil olmasını təmin etmək üçün yaradılır. Bu son endpoints istifadəçinin etimadnamələrini (məsələn, istifadəçi adı və parol) göndərməklə POST sorğu edir və onun cavabını alır. Bu endpoints-i istifadə etmək üçün "useLoginMutation" funksiyası export edilir və istifadə olunur.
- authApi.js
import { tokenSlice } from "../slice/tokenSlice";
const authApi = tokenSlice.injectEndpoints({
endpoints: builder => ({
login: builder.mutation({
query: credentials => ({
url: 'Account/Login',
method: 'POST',
body: { ...credentials }
})
}),
})
})
export const { useLoginMutation } = authApi
export { authApi }
- store/index.js -də "reducer" sahəsində "auth" və "tokenSlice" adlı iki fərqli reducer müəyyən edilir. Bu reducerlər tətbiqdə state idarəetməsini təmin edir. Middleware sahəsində redux-toolkit-dən "getDefaultMiddleware" funksiyası çağırılır və tokenSlice.middleware əlavə edilir. "DevTools" sahəsi "doğru" olaraq təyin edilmişdir, belə ki, tətbiqin inkişafı zamanı brauzer vasitəsilə 'store'-nin vəziyyətini izləyə bilərik. "setupListeners" funksiyası "store.dispatch" ilə də çağırılır. Nəhayət, "useLoginMutation" üçün "authApi" faylından import edilir. Bu giriş əməliyyatını yerinə yetirmək üçün istifadə edilən funksiyadır.
- store/index.js
import { configureStore } from "@reduxjs/toolkit";
import { setupListeners } from '@reduxjs/toolkit/dist/query'
import { authReducer } from "./slice/authSlice";
import { tokenSlice } from "./slice/tokenSlice";
export const store = configureStore({
reducer: {
auth: authReducer,
[tokenSlice.reducerPath]: tokenSlice.reducer,
},
middleware: (getDefaultMiddleware) => {
return getDefaultMiddleware()
.concat(tokenSlice.middleware);
},
devTools: true
})
setupListeners(store.dispatch);
export { useLoginMutation } from './api/authApi'
- Login olmadan digər səhifələrə keçmesini blok etmək üçün Layout folderində 'RequireAuth.js' faylı yaradırıq.
Komponent istifadəçinin 'token' adlı dəyişəndən istifadə edərək daxil olub-olmadığını yoxlayır. İstifadəçi daxil olubsa, məzmun Outlet komponenti ilə göstərilir. İstifadəçi daxil deyilsə, Navigate komponenti ilə istifadəçinin giriş səhifəsinə yönləndirilir. 'from' adlı dəyişən ilə istifadəçinin hansı səhifədən gəldiyi məlumat da yönləndirmə prosesi zamanı vəziyyət kimi göndərilir. Bu yolla istifadəçi daxil olduqdan sonra yenidən birinci səhifəyə yönləndirilə bilər. Sonra isə hansı səhifələrin bloklanacağını təyin etmək üçün App.js -də quraşdırırq.
- RequireAuth.js
import React from 'react'
import { Navigate, Outlet, useLocation } from 'react-router-dom';
const RequireAuth = () => {
const token = localStorage.getItem('token');
const location = useLocation();
return (
token ? <Outlet /> : <Navigate to='/login' state={{ from: location }} replace />
)
}
export default RequireAuth
- App.js
<BrowserRouter>
<Routes>
<Route path='/' element={<RequireAuth />}>
<Route element={<SharedLayout setMode={setMode} mode={mode} setDevMode={setDevMode} devMode={devMode} />}>
<Route index element={<Test1 />} />
<Route path='users' element={<User />} />
<Route path='groups' element={<Group />} />
<Route path='vendors' element={<Vendor />} />
<Route path='requests' element={<Request />} />
<Route path='payments' element={<Payment />} />
<Route path='settings' element={<Setting />} />
<Route path='approvalstage' element={<ApprovalStage />} />
<Route path='test2' element={<Test2 />} />
<Route path='profile' element={<Profile />} />
</Route>
</Route>
<Route path='login' element={<Login />} />
<Route path='register' element={<Register />} />
<Route path='*' element={<ErrorPage />} />
</Routes>
</BrowserRouter>
- Lazım olan hər şey əldə olunduqdan sonra Login componentini yaza bilərik. (/Authentication/Login.js)
- API-ləri rahat çəkə bilərik. Redux Toolkit-in "query" kitabxanasından istifadə edərək API yaradılır. Əvvəlcə createApi funksiyası ilə API obyekti yaradılır. Bu obyektin daxilində bir neçə parametr daxil edilib: reducerPath: Bu API-nin istifadə etdiyi reducer-in yolunu müəyyən edir. baseQuery: API çağırmaq üçün istifadə ediləcək əsas sorğu obyektini təyin edir. Bu sorğu obyekti adətən hər bir API çağırışı üçün istifadə ediləcək ümumi parametrləri əhatə edə bilər. endpoints: Bu API-nin xidmət edə biləcəyi sorğuları və ya əməliyyatları müəyyən edir. Burada yalnız bir sorğu müəyyən edilir: getMenu. Bu sorğu Menu/GetUserMenusWithChilds-ə GET sorğusu verir. Nəhayət, useGetMenuQuery hook-u və menuApi obyekti menuApi obyektindən çıxır. Bu hook komponentlərdə getMenu sorğusundan istifadə etmək üçün istifadə edilə bilər.
- menuApi.js
import { createApi } from "@reduxjs/toolkit/dist/query/react";
import { createBaseQuery } from "../helper/createBaseQuery";
const menuApi = createApi({
reducerPath: 'menu',
baseQuery: createBaseQuery(),
endpoints(builder) {
return {
getMenu: builder.query({
query: () => {
return {
url: 'Menu/GetUserMenusWithChilds',
method: 'GET'
}
}
})
}
}
})
export const { useGetMenuQuery } = menuApi;
export { menuApi };
- store/index.js -dən işə salınır.
- index.js
import { configureStore } from "@reduxjs/toolkit";
import { setupListeners } from '@reduxjs/toolkit/dist/query'
import { menuApi } from "./api/menuApi";
import { authReducer } from "./slice/authSlice";
import { tokenSlice } from "./slice/tokenSlice";
export const store = configureStore({
reducer: {
menu: menuApi.reducer,
auth: authReducer,
[tokenSlice.reducerPath]: tokenSlice.reducer,
},
middleware: (getDefaultMiddleware) => {
return getDefaultMiddleware()
.concat(menuApi.middleware)
.concat(tokenSlice.middleware);
},
devTools: true
})
setupListeners(store.dispatch);
export { useGetMenuQuery } from './api/menuApi';
export { useLoginMutation } from './api/authApi'
- Artıq Componentdə həmin dataları çağırıb istifadə edə bilərik.
const { data, isLoading, error } = useGetMenuQuery()
- store/action/refresh.js
import { createAction } from '@reduxjs/toolkit';
export const refresh = createAction("REFRESH");
- slice
import { createSlice } from "@reduxjs/toolkit";
import { refresh } from "../action/refresh";
const refreshSlice = createSlice({
name: "refresh",
initialState: {},
reducers: {},
extraReducers: {
[refresh]: () => {
alert("HELLO");
}
}
})
export const refreshReducer = refreshSlice.reducer
<Box sx={{ display: 'flex', flexWrap: 'wrap' }}> <Box sx={{ p: 1, m: 1, }}> div 1 <Box sx={{ p: 1, m: 1, }}> div 2