Skip to content

Commit

Permalink
admin should manage user role
Browse files Browse the repository at this point in the history
[Delivers #101]
Co-authored-by: AMBROISE Muhayimana <107347030+ambroisegithub@users.noreply.github.com>
  • Loading branch information
jkarenzi authored and ambroisegithub committed Jul 22, 2024
1 parent 4a355ef commit f9bf280
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/app/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import buyerSlice from '@/app/Dashboard/buyerSlice';
import orderSlice from './Dashboard/orderSlice';

import ordersSliceReducer from '@/features/Orders/ordersSlice';
import userRoleSlice from '@/features/userRole/userRoleSlice';

export const store = configureStore({
reducer: {
Expand All @@ -35,6 +36,7 @@ export const store = configureStore({
orders: ordersSliceReducer,
product: addProductSlice,
DeshboardProducts: dashboardProductsSlice,
userRoles: userRoleSlice,
},
});

Expand Down
15 changes: 15 additions & 0 deletions src/components/dashBoard/DashboardSideNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Box,
ChevronDown,
ChevronRight,
User,
} from 'lucide-react';

const sideBarItems = [
Expand Down Expand Up @@ -47,6 +48,20 @@ const sideBarItems = [
},
],
},
{
name: 'User Role',
icon: <User className="icon" />,
subItems: [
{
path: '/dashboard/userRole',
name: 'All Roles',
},
{
path: '/products/addRole',
name: 'Add Role',
},
],
},
];

interface SideBarItemProps {
Expand Down
141 changes: 141 additions & 0 deletions src/components/dashBoard/TableUserRole.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/* eslint-disable jsx-a11y/control-has-associated-label */
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { FaRegTrashAlt } from 'react-icons/fa';
import { MdOutlineEdit } from 'react-icons/md';
import { RootState, AppDispatch } from '@/app/store';
import {
fetchUserRoles,
deleteUserRole,
} from '@/features/userRole/userRoleSlice';
import ConfirmationCard from './ConfirmationCard';
import CircularPagination from './NavigateonPage';

function TableUserRole() {
const dispatch: AppDispatch = useDispatch();
const { roles, status } = useSelector((state: RootState) => state.userRoles);

const [isConfirmationModalVisible, setModalVisible] = useState(false);
const [itemSelected, setItemSelected] = useState<number | null>(null);
const [mode, setMode] = useState('');

useEffect(() => {
dispatch(fetchUserRoles());
}, [dispatch]);

const handleDelete = (id: number) => {
setItemSelected(id);
setMode('delete');
setModalVisible(true);
};

const confirmDelete = async () => {
if (itemSelected !== null) {
await dispatch(deleteUserRole(itemSelected));
setModalVisible(false);
}
};

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const handleUpdate = (_id: number) => {
// Implement update functionality
};

const confirmUpdate = () => {
if (itemSelected !== null) {
// Implement update logic
}
setModalVisible(false);
};

return (
<div className="bg-[#F5F6F6]">
<div className="flex flex-row justify-between items-center pl-[2.5%] mb-[1.3%] mt-[0.8%]">
<div className="font-bold text-lg mr-[15px]">User Roles</div>
</div>
<div className="bg-white min-w-full min-h-full pr-[15px] pl-[2.5%] pt-[3.5%] rounded-xl">
<table className="min-w-full min-h-full">
<thead className="text-[12px] leading-[26px]">
<tr className="bg-[#F5F6F6] rounded-[10px]">
<th className="px-4 text-left text-grey text-[12px] leading-[26px]">
ID
</th>
<th className="px-4 text-left text-grey text-[12px] leading-[26px]">
Role Name
</th>
<th className="px-4 text-left text-grey text-[12px] leading-[26px]">
Action
</th>
</tr>
<tr className="h-[20px]"></tr>
</thead>
<tbody>
{status === 'loading' &&
Array(6)
.fill(null)
.map((_, index) => (
<tr
key={index}
className="border-b-[2.5px] min-w-full h-[60px] border-[#F5F6F6] last:border-none"
>
<td colSpan={3}>
<div className="shadow-lg animate-pulse bg-slate-300 rounded-lg h-[40px]"></div>
</td>
</tr>
))}
{status === 'failed' &&
Array(6)
.fill(null)
.map((_, index) => (
<tr
key={index}
className="border-b-[2.5px] min-w-full h-[60px] border-[#F5F6F6] last:border-none"
>
<td colSpan={3}>
<div className="shadow-lg bg-red-100 rounded-lg h-[40px] text-center text-red-500">
Failed to load roles...
</div>
</td>
</tr>
))}
{status === 'succeeded' &&
roles &&
roles.map((role) => (
<tr
key={role.id}
className="h-[68px] text-grey leading-[16px] text-[12px] font-normal border-b-[2px] border-[#F5F6F6] last:border-none hover:translate-x-[0.7px] hover:border hover:bg-[#F5F6F6]"
>
<td className="px-4">{role.id}</td>
<td className="px-4">{role.name}</td>
<td className="px-4 flex gap-2">
<MdOutlineEdit
className="cursor-pointer"
onClick={() => handleUpdate(role.id)}
/>
<FaRegTrashAlt
className="cursor-pointer"
onClick={() => handleDelete(role.id)}
/>
</td>
</tr>
))}
</tbody>
</table>
{isConfirmationModalVisible && (
<ConfirmationCard
onConfirm={mode === 'delete' ? confirmDelete : confirmUpdate}
onCancel={() => setModalVisible(false)}
mode={mode}
/>
)}
<CircularPagination
totalPages={Math.ceil((roles?.length ?? 0) / 6)}
currentPage={1}
onPageChange={() => {}}
/>
</div>
</div>
);
}

export default TableUserRole;
71 changes: 71 additions & 0 deletions src/features/userRole/userRoleSlice.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import axios from 'axios';

interface UserRole {
id: number;
name: string;
}

interface UserRoleState {
roles: UserRole[];
status: 'idle' | 'loading' | 'succeeded' | 'failed';
error: string | null;
}

const initialState: UserRoleState = {
roles: [],
status: 'idle',
error: null,
};

export const fetchUserRoles = createAsyncThunk(
'userRoles/fetchUserRoles',
async () => {
const response = await axios.get(
`${import.meta.env.VITE_BASE_URL}/roles/get_roles`
);
console.log(response.data.roles);
return response.data.roles;
}
);

export const deleteUserRole = createAsyncThunk(
'userRoles/deleteUserRole',
async (id: number) => {
await axios.delete(`/api/roles/${id}`);
return id;
}
);

const userRoleSlice = createSlice({
name: 'userRoles',
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchUserRoles.pending, (state) => {
state.status = 'loading';
})
.addCase(
fetchUserRoles.fulfilled,
(state, action: PayloadAction<UserRole[]>) => {
state.status = 'succeeded';
state.roles = action.payload;
}
)
.addCase(fetchUserRoles.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message || 'Failed to fetch roles';
})
.addCase(
deleteUserRole.fulfilled,
(state, action: PayloadAction<number>) => {
state.roles = state.roles.filter(
(role) => role.id !== action.payload
);
}
);
},
});

export default userRoleSlice.reducer;
2 changes: 2 additions & 0 deletions src/routes/AppRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { Orders } from '@/components/Orders/Orders';
import AddProducts from '@/components/dashBoard/addProducts';
import ProductDetails from '@/pages/ProductDetails';
import ProtectedRoute from '@/components/ProtectedRoute';
import TableUserRole from '@/components/dashBoard/TableUserRole';

function AppRoutes() {
return (
Expand Down Expand Up @@ -62,6 +63,7 @@ function AppRoutes() {
element={<EditProductPage />}
/>
<Route index path="/dashboard/addProduct/" element={<AddProducts />} />
<Route index path="/dashboard/userRole/" element={<TableUserRole />} />
</Route>
<Route path="*" element={<ErrorPage />} />
<Route path="/forgot-password" element={<PasswordResetRequestForm />} />
Expand Down

0 comments on commit f9bf280

Please sign in to comment.