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 { renderRoutesRecursively } from "./Components/Routes/RenderRoutesRecursively";
import { RenderRouteElement } from "./Components/Routes/RenderRouteElement";
import { UserTypeEnum } from "./enums/UserType";
import { RoleByType } from "./utils/RoleByType";
const Page404 = lazy(() => import("./Layout/Ui/NotFoundPage"));
const Auth = lazy(() => import("./Pages/Auth/Page"));
const App = () => {
return (
<Routes>
<Route
@ -31,10 +33,14 @@ const App = () => {
}
/>
{renderRoutesRecursively(menuItems)}
{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) {
return false;
}

View File

@ -3,10 +3,17 @@ import { TMenuItem } from "../../types/App";
import { hasAbility } from "../../utils/hasAbility";
import { Route } from "react-router-dom";
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[]) =>
routes.map((route: TMenuItem) => {
const useAbility = hasAbility(route.abilities, route.abilities_value);
if(!RoleByType(route)){
return false ;
}
if (!useAbility) {
return false;
}

View File

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

View File

@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect } from "react";
import { Formik } from "formik";
import useAuthState from "../../zustand/AuthState";
import useNavigateOnSuccess from "../../Hooks/useNavigateOnSuccess";
@ -8,6 +8,9 @@ import { initialValues } from "./formutils";
import { FormValues } from "../../types/Auth";
import { toast } from "react-toastify";
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 { mutate, isLoading, isSuccess, data } = useLoginAdmin();
@ -16,12 +19,28 @@ const LoginForm = () => {
mutate(values);
};
const { login } = useAuthState();
const { login,isAuthenticated} = useAuthState();
const LoginData = {
...data,
} as any;
useNavigateOnSuccess(isSuccess, "/", () => login(LoginData?.data as any));
} 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 (
<div className="LoginForm">
<Formik initialValues={initialValues} onSubmit={handelSubmit}>

View File

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

View File

@ -5,17 +5,23 @@ export const getInitialValues = (
objectToEdit: Partial<Student>,
): StudentInitialValues => {
return {
id: objectToEdit?.id,
id: objectToEdit?.user_id,
first_name: objectToEdit?.first_name ?? "",
last_name: objectToEdit?.last_name ?? "",
username: objectToEdit?.user?.username ?? null ,
password: null ,
// address: objectToEdit?.address ?? "",
// birthday: objectToEdit?.birthday ?? "",
// city: objectToEdit?.city ?? "",
grade_id: objectToEdit?.grade_id ,
// image: objectToEdit?.image ?? "",
sex: objectToEdit?.sex ,
};
};
export const getValidationSchema = () => {
// validate input
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,
} from "../../utils/hasAbilityFn";
import ActionButtons from "../../Components/Table/ActionButtons";
import ColumnsImage from "../../Components/Columns/ColumnsImage";
export const useColumns = () => {
const { handel_open_model } = useModalHandler();
@ -22,7 +21,7 @@ export const useColumns = () => {
const navigate = useNavigate();
const handelShow = (record: Student) => {
navigate(`${record?.id}`);
navigate(`${record?.user_id}`);
};
const handelDelete = (data: Student) => {
@ -42,15 +41,31 @@ export const useColumns = () => {
dataIndex: "id",
key: "id",
align: "center",
render: (_text, record) => record?.id,
render: (_text, record) => record?.user_id,
},
{
title: `${t("columns.name")}`,
dataIndex: "name",
key: "name",
title: `${t("columns.first_name")}`,
dataIndex: "first_name",
key: "first_name",
align: "center",
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 ? (
<button

View File

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

View File

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

View File

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

View File

@ -1,51 +1,34 @@
import { Nullable } from "./App";
import { DateType, Nullable } from "./App";
// 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 {
id: number;
user: StudentUser;
first_name: string;
last_name: string;
location: StudentLocation;
contact_info: ContactInfo;
contact_number1 : string | number
contact_number2 : string | number
first_name: string; // The first name of the user
last_name: string; // The last name of the user
city: string | null; // The city of the user, can be null
sex: string; // The sex of the user, using a union type for possible values
image: string | null; // The URL of the user's image, can be null
address: string | null; // The address of the user, can be null
card: string | null; // The card information, can be null
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 {
id: number;
user: StudentUser;
first_name: string;
last_name: string;
location: StudentLocation;
lat: string | Number;
lng: string | Number;
contact_info: ContactInfo;
contact_number1 : string | number
contact_number2 : string | number
username : string;
password:any
first_name: string; // The first name of the user
last_name: string; // The last name of the user
city: string | null; // The city of the user, can be null
sex: string; // The sex of the user, using a union type for possible values
image: string | null; // The URL of the user's image, can be null
address: string | null; // The address of the user, can be null
card: string | null; // The card information, can be null
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 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 { 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 {
token: string | null | undefined;
abilities: any;
isAuthenticated: boolean;
login: (Data: any) => Promise<void>;
logout: () => Promise<void>;
}
@ -14,9 +13,11 @@ interface AuthStore {
const useAuthState = create<AuthStore>((set) => {
const storedToken = localStorage.getItem(TOKEN_KEY);
const storedAbilities = localStorage.getItem(ABILITIES_KEY);
return {
isAuthenticated: true,
const storedType = getLocalStorage(USER_KEY) ;
console.log(RoleByType(storedType));
return {
isAuthenticated: !!storedToken,
token: storedToken,
abilities: storedAbilities,