add protected route

This commit is contained in:
karimaldeen 2024-08-21 17:30:31 +03:00
parent ee5a3b7b14
commit a5f54f1794
13 changed files with 155 additions and 76 deletions

View File

@ -5,11 +5,13 @@ import { Spin } from "antd";
import { hasAbility } from "./utils/hasAbility"; import { hasAbility } from "./utils/hasAbility";
import { renderRoutesRecursively } from "./Components/Routes/RenderRoutesRecursively"; import { renderRoutesRecursively } from "./Components/Routes/RenderRoutesRecursively";
import { RenderRouteElement } from "./Components/Routes/RenderRouteElement"; import { RenderRouteElement } from "./Components/Routes/RenderRouteElement";
import { UserTypeEnum } from "./enums/UserType";
import { RoleByType } from "./utils/RoleByType";
const Page404 = lazy(() => import("./Layout/Ui/NotFoundPage")); const Page404 = lazy(() => import("./Layout/Ui/NotFoundPage"));
const Auth = lazy(() => import("./Pages/Auth/Page")); const Auth = lazy(() => import("./Pages/Auth/Page"));
const App = () => { const App = () => {
return ( return (
<Routes> <Routes>
<Route <Route
@ -31,10 +33,14 @@ const App = () => {
} }
/> />
{renderRoutesRecursively(menuItems)} {renderRoutesRecursively(menuItems)}
{CrudRoute.map((route) => { {CrudRoute.map((route) => {
const useAbility = hasAbility(route.abilities, route.abilities_value); const useAbility = hasAbility(route.abilities, route.abilities_value);
if(!RoleByType(route)){
return false ;
}
if (!useAbility) { if (!useAbility) {
return false; return false;
} }

View File

@ -3,10 +3,17 @@ import { TMenuItem } from "../../types/App";
import { hasAbility } from "../../utils/hasAbility"; import { hasAbility } from "../../utils/hasAbility";
import { Route } from "react-router-dom"; import { Route } from "react-router-dom";
import { RenderRouteElement } from "./RenderRouteElement"; import { RenderRouteElement } from "./RenderRouteElement";
import { UserTypeEnum } from "../../enums/UserType";
import Item from "antd/es/list/Item";
import { RoleByType } from "../../utils/RoleByType";
export const renderRoutesRecursively = (routes: TMenuItem[]) => export const renderRoutesRecursively = (routes: TMenuItem[]) =>
routes.map((route: TMenuItem) => { routes.map((route: TMenuItem) => {
const useAbility = hasAbility(route.abilities, route.abilities_value); const useAbility = hasAbility(route.abilities, route.abilities_value);
if(!RoleByType(route)){
return false ;
}
if (!useAbility) { if (!useAbility) {
return false; return false;
} }

View File

@ -9,6 +9,8 @@ import { useTranslation } from "react-i18next";
import { getLocalStorage } from "../../utils/LocalStorage"; import { getLocalStorage } from "../../utils/LocalStorage";
import { BRANCH_OBJECT_KEY } from "../../config/AppKey"; import { BRANCH_OBJECT_KEY } from "../../config/AppKey";
import { MenuItem } from "../../Components/Layout/SideBar/MenuItem"; import { MenuItem } from "../../Components/Layout/SideBar/MenuItem";
import { UserTypeEnum } from "../../enums/UserType";
import { RoleByType } from "../../utils/RoleByType";
const SideBar = () => { const SideBar = () => {
const location = useLocation(); const location = useLocation();
@ -17,7 +19,6 @@ const SideBar = () => {
const { logout } = useAuthState(); const { logout } = useAuthState();
const [t] = useTranslation(); const [t] = useTranslation();
const branch_name = getLocalStorage(BRANCH_OBJECT_KEY)?.name; const branch_name = getLocalStorage(BRANCH_OBJECT_KEY)?.name;
return ( return (
<div className="side_bar"> <div className="side_bar">
<h1> <h1>
@ -31,6 +32,11 @@ const SideBar = () => {
if (!useAbility) { if (!useAbility) {
return <React.Fragment key={index}></React.Fragment>; return <React.Fragment key={index}></React.Fragment>;
} }
if(!RoleByType(item)){
return <React.Fragment key={index}></React.Fragment> ;
}
return ( return (
<MenuItem <MenuItem
key={index} key={index}

View File

@ -1,4 +1,4 @@
import React from "react"; import React, { useEffect } from "react";
import { Formik } from "formik"; import { Formik } from "formik";
import useAuthState from "../../zustand/AuthState"; import useAuthState from "../../zustand/AuthState";
import useNavigateOnSuccess from "../../Hooks/useNavigateOnSuccess"; import useNavigateOnSuccess from "../../Hooks/useNavigateOnSuccess";
@ -8,6 +8,9 @@ import { initialValues } from "./formutils";
import { FormValues } from "../../types/Auth"; import { FormValues } from "../../types/Auth";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { getLocalStorage } from "../../utils/LocalStorage";
import { USER_KEY } from "../../config/AppKey";
const LoginForm = () => { const LoginForm = () => {
const { mutate, isLoading, isSuccess, data } = useLoginAdmin(); const { mutate, isLoading, isSuccess, data } = useLoginAdmin();
@ -16,11 +19,27 @@ const LoginForm = () => {
mutate(values); mutate(values);
}; };
const { login } = useAuthState(); const { login,isAuthenticated} = useAuthState();
const LoginData = { const LoginData = {
...data, ...data,
} as any; } as any;
useNavigateOnSuccess(isSuccess, "/", () => login(LoginData?.data as any)); const navigate = useNavigate()
const LocalType = getLocalStorage(USER_KEY)?.type ?? false ;
useEffect(() => {
if(isSuccess){
login(LoginData?.data as any)
}
}, [isSuccess])
useEffect(() => {
if(LocalType ){
window.location.href = ("/")
}
}, [LocalType])
return ( return (
<div className="LoginForm"> <div className="LoginForm">

View File

@ -18,7 +18,10 @@ const Form = ({ isEdit = false }: { isEdit?: boolean }) => {
const canChangeGradePage = !!Grade?.links?.next; const canChangeGradePage = !!Grade?.links?.next;
const GradePage = Grade?.meta?.currentPage; const GradePage = Grade?.meta?.currentPage;
const sex = [
{name:"male" , id :"male"},
{name:"female" , id :"female"}
]
return ( return (
<Row className="w-100"> <Row className="w-100">
<Col> <Col>
@ -45,6 +48,7 @@ const Form = ({ isEdit = false }: { isEdit?: boolean }) => {
page={GradePage} page={GradePage}
/> />
<ValidationField type="Select" name="sex" option={sex} />
</Col> </Col>
</Row> </Row>

View File

@ -5,17 +5,23 @@ export const getInitialValues = (
objectToEdit: Partial<Student>, objectToEdit: Partial<Student>,
): StudentInitialValues => { ): StudentInitialValues => {
return { return {
id: objectToEdit?.id, id: objectToEdit?.user_id,
first_name: objectToEdit?.first_name ?? "", first_name: objectToEdit?.first_name ?? "",
last_name: objectToEdit?.last_name ?? "", last_name: objectToEdit?.last_name ?? "",
username: objectToEdit?.user?.username ?? null , // address: objectToEdit?.address ?? "",
password: null , // birthday: objectToEdit?.birthday ?? "",
// city: objectToEdit?.city ?? "",
grade_id: objectToEdit?.grade_id ,
// image: objectToEdit?.image ?? "",
sex: objectToEdit?.sex ,
}; };
}; };
export const getValidationSchema = () => { export const getValidationSchema = () => {
// validate input // validate input
return Yup.object().shape({ return Yup.object().shape({
name: Yup.string().required("validation.required"), first_name: Yup.string().required("validation.required"),
last_name: Yup.string().required("validation.required"),
}); });
}; };

View File

@ -13,7 +13,6 @@ import {
canShowStudent, canShowStudent,
} from "../../utils/hasAbilityFn"; } from "../../utils/hasAbilityFn";
import ActionButtons from "../../Components/Table/ActionButtons"; import ActionButtons from "../../Components/Table/ActionButtons";
import ColumnsImage from "../../Components/Columns/ColumnsImage";
export const useColumns = () => { export const useColumns = () => {
const { handel_open_model } = useModalHandler(); const { handel_open_model } = useModalHandler();
@ -22,7 +21,7 @@ export const useColumns = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const handelShow = (record: Student) => { const handelShow = (record: Student) => {
navigate(`${record?.id}`); navigate(`${record?.user_id}`);
}; };
const handelDelete = (data: Student) => { const handelDelete = (data: Student) => {
@ -42,15 +41,31 @@ export const useColumns = () => {
dataIndex: "id", dataIndex: "id",
key: "id", key: "id",
align: "center", align: "center",
render: (_text, record) => record?.id, render: (_text, record) => record?.user_id,
}, },
{ {
title: `${t("columns.name")}`, title: `${t("columns.first_name")}`,
dataIndex: "name", dataIndex: "first_name",
key: "name", key: "first_name",
align: "center", align: "center",
render: (_text, record) => record?.first_name, render: (_text, record) => record?.first_name,
}, },
{
title: `${t("columns.last_name")}`,
dataIndex: "last_name",
key: "last_name",
align: "center",
render: (_text, record) => record?.last_name,
},
{
title: `${t("columns.sex")}`,
dataIndex: "sex",
key: "sex",
align: "center",
render: (_text, record) => record?.sex,
},
{ {
title: canAddStudent ? ( title: canAddStudent ? (
<button <button

View File

@ -31,6 +31,7 @@ import { hasAbility } from "./utils/hasAbility";
import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "./enums/abilities"; import { ABILITIES_ENUM, ABILITIES_VALUES_ENUM } from "./enums/abilities";
import { ParamsEnum } from "./enums/params"; import { ParamsEnum } from "./enums/params";
import { BsPeople } from "react-icons/bs"; import { BsPeople } from "react-icons/bs";
import { UserTypeEnum } from "./enums/UserType";
export const menuItems: TMenuItem[] = [ export const menuItems: TMenuItem[] = [
{ {
@ -42,6 +43,7 @@ export const menuItems: TMenuItem[] = [
abilities: ABILITIES_ENUM?.PASS, abilities: ABILITIES_ENUM?.PASS,
abilities_value: ABILITIES_VALUES_ENUM.INDEX, abilities_value: ABILITIES_VALUES_ENUM.INDEX,
prevPath: 0, prevPath: 0,
type:UserTypeEnum?.PASS
}, },
{ {
@ -105,16 +107,17 @@ export const menuItems: TMenuItem[] = [
abilities_value: ABILITIES_VALUES_ENUM.INDEX, abilities_value: ABILITIES_VALUES_ENUM.INDEX,
prevPath: 0, prevPath: 0,
}, },
// { {
// header: "page_header.studentPackage", header: "page_header.studentPackage",
// element: <StudentPackage />, element: <StudentPackage />,
// icon: <BsPeople />, icon: <BsPeople />,
// text: "sidebar.studentPackage", text: "sidebar.studentPackage",
// path: `/${ABILITIES_ENUM?.STUDENT_PACKAGE}`, path: `/${ABILITIES_ENUM?.STUDENT_PACKAGE}`,
// abilities: ABILITIES_ENUM?.STUDENT_PACKAGE, abilities: ABILITIES_ENUM?.STUDENT_PACKAGE,
// abilities_value: ABILITIES_VALUES_ENUM.INDEX, abilities_value: ABILITIES_VALUES_ENUM.INDEX,
// prevPath: 0, prevPath: 0,
// }, type:UserTypeEnum.RE_SELLER
},
]; ];
export const CrudRoute: TCrudRoute[] = [ export const CrudRoute: TCrudRoute[] = [

View File

@ -1,5 +1,6 @@
export enum UserTypeEnum { export enum UserTypeEnum {
ADMIN = "ADMIN", ADMIN = "admin",
RE_SELLER = "RE_SELLER", RE_SELLER = "reseller",
PASS="pass"
} }

View File

@ -40,7 +40,7 @@ export type TCrudRoute = {
element: ReactElement | LazyExoticComponent<any>; element: ReactElement | LazyExoticComponent<any>;
abilities: ABILITIES_ENUM; abilities: ABILITIES_ENUM;
abilities_value: ABILITIES_VALUES_ENUM; abilities_value: ABILITIES_VALUES_ENUM;
type?:ABILITIES_ENUM type?:UserTypeEnum
prevPath: number; prevPath: number;
}; };

View File

@ -1,51 +1,34 @@
import { Nullable } from "./App"; import { DateType, Nullable } from "./App";
// Define the Teacher interface // Define the Teacher interface
interface StudentUser {
id: number;
username: string;
phone_number: string | null;
type: 'Student' | 'other'; // Specify other types if needed
}
interface StudentLocation {
lat: string;
lng: string;
}
interface ContactInfo {
contact_number1: string;
contact_number2: string;
card_number: string | null;
}
export interface Student { export interface Student {
id: number; first_name: string; // The first name of the user
user: StudentUser; last_name: string; // The last name of the user
first_name: string; city: string | null; // The city of the user, can be null
last_name: string; sex: string; // The sex of the user, using a union type for possible values
location: StudentLocation; image: string | null; // The URL of the user's image, can be null
contact_info: ContactInfo; address: string | null; // The address of the user, can be null
contact_number1 : string | number card: string | null; // The card information, can be null
contact_number2 : string | number birthday: DateType; // The birthday of the user, can be null
grade_id: number | string; // The ID of the user's grade
user_id: number; // The unique ID of the user
} }
export interface InitialValues { export interface InitialValues {
id: number; id: number;
user: StudentUser; first_name: string; // The first name of the user
first_name: string; last_name: string; // The last name of the user
last_name: string; city: string | null; // The city of the user, can be null
location: StudentLocation; sex: string; // The sex of the user, using a union type for possible values
lat: string | Number; image: string | null; // The URL of the user's image, can be null
lng: string | Number; address: string | null; // The address of the user, can be null
contact_info: ContactInfo; card: string | null; // The card information, can be null
contact_number1 : string | number birthday: DateType; // The birthday of the user, can be null
contact_number2 : string | number grade_id: number | string; // The ID of the user's grade
username : string; user_id: number; // The unique ID of the user
password:any
} }
export type StudentInitialValues = Partial<Nullable<InitialValues>>; export type StudentInitialValues = Partial<Nullable<InitialValues>>;

28
src/utils/RoleByType.ts Normal file
View File

@ -0,0 +1,28 @@
import { USER_KEY } from "../config/AppKey";
import { UserTypeEnum } from "../enums/UserType";
import { getLocalStorage } from "./LocalStorage";
export const RoleByType = (item: { type?: string }):boolean=>{
const type = item?.type ?? UserTypeEnum.ADMIN;
const LocalType = getLocalStorage(USER_KEY)?.type ?? undefined ;
const isAdmin = LocalType === UserTypeEnum.ADMIN ;
const isReSeller = LocalType === UserTypeEnum.RE_SELLER;
const isAdminRoute = type === UserTypeEnum.ADMIN ;
const isReSellerRoute = type === UserTypeEnum.RE_SELLER;
console.log(LocalType);
if(!LocalType){
return false
}
if(type === UserTypeEnum.PASS) { return true } ;
if(isAdmin && isReSellerRoute ){
return false ;
}
if(isReSeller && !isReSellerRoute ){
return false ;
}
return true;
}

View File

@ -1,12 +1,11 @@
import { create } from "zustand"; import { create } from "zustand";
import { ABILITIES_KEY, TOKEN_KEY, USER_KEY } from "../config/AppKey"; import { ABILITIES_KEY, TOKEN_KEY, USER_KEY } from "../config/AppKey";
import { useNavigate } from "react-router-dom"; import { RoleByType } from "../utils/RoleByType";
import { getLocalStorage } from "../utils/LocalStorage";
interface AuthStore { interface AuthStore {
token: string | null | undefined; token: string | null | undefined;
abilities: any; abilities: any;
isAuthenticated: boolean; isAuthenticated: boolean;
login: (Data: any) => Promise<void>; login: (Data: any) => Promise<void>;
logout: () => Promise<void>; logout: () => Promise<void>;
} }
@ -14,9 +13,11 @@ interface AuthStore {
const useAuthState = create<AuthStore>((set) => { const useAuthState = create<AuthStore>((set) => {
const storedToken = localStorage.getItem(TOKEN_KEY); const storedToken = localStorage.getItem(TOKEN_KEY);
const storedAbilities = localStorage.getItem(ABILITIES_KEY); const storedAbilities = localStorage.getItem(ABILITIES_KEY);
const storedType = getLocalStorage(USER_KEY) ;
console.log(RoleByType(storedType));
return { return {
isAuthenticated: true, isAuthenticated: !!storedToken,
token: storedToken, token: storedToken,
abilities: storedAbilities, abilities: storedAbilities,