Skip to content

Commit

Permalink
fixing some bugs for better appearance
Browse files Browse the repository at this point in the history
  • Loading branch information
princenzmw committed Jul 19, 2024
1 parent 3fb8f9d commit 7104c0f
Show file tree
Hide file tree
Showing 14 changed files with 598 additions and 45 deletions.
22 changes: 6 additions & 16 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,14 @@ import AdminPage from './pages/admin';
import Category from './pages/admin/Category';
import Sellers from './pages/admin/Sellers';
import Buyers from './pages/admin/Buyers';
import Messages from './pages/admin/Messages';
import UserManagement from './pages/admin/UserManagement';
import NotFoundPage from './pages/NotFoundPage';
import Settings from './pages/admin/Settings';
import SellerSettings from './pages/seller/Settings';
import SellersPage from './pages/seller';
import Orders from './pages/seller/Orders';
import Products from './pages/seller/Products';
import Customers from './pages/seller/Customers';
import SellerMessages from './pages/seller/Messages';
import SellerSettings from './pages/seller/Settings';
import AdminSettings from './pages/admin/Settings';
import AddNewProduct from './pages/seller/AddNewProduct';
import RestrictedSellerRoute from './components/dashboard/RestrictedSellerLayout';

Expand Down Expand Up @@ -140,12 +138,12 @@ const App = () => {
element: <Buyers />,
},
{
path: 'messages',
element: <SellerMessages />,
path: 'users',
element: <UserManagement />,
},
{
path: 'settings',
element: <SellerSettings />,
element: <AdminSettings />,
},
],
},
Expand Down Expand Up @@ -173,17 +171,9 @@ const App = () => {
path: 'customers',
element: <Customers />,
},
{
path: 'messages',
element: <Messages />,
},
{
path: 'users',
element: <UserManagement />,
},
{
path: 'settings',
element: <Settings />,
element: <SellerSettings />,
},
],
},
Expand Down
6 changes: 3 additions & 3 deletions src/components/admin/AdminLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { Outlet } from 'react-router-dom';
import Sidebar from '../dashboard/Sidebar';
import { RxDashboard } from 'react-icons/rx';
import { FaCog, FaRegListAlt, FaUserFriends } from 'react-icons/fa';
import { FaRegEnvelope, FaUserTie } from 'react-icons/fa6';
import { FaCog, FaRegListAlt, FaUserFriends, FaUsers } from 'react-icons/fa';
import { FaUserTie } from 'react-icons/fa6';

export default function AdminLayout() {
const adminSidebarLinks = [
{ name: 'Dashboard', path: '/admin', icon: <RxDashboard className='mr-3' /> },
{ name: 'Categories', path: 'categories', icon: <FaRegListAlt className='mr-3' /> },
{ name: 'Sellers', path: 'sellers', icon: <FaUserFriends className='mr-3' /> },
{ name: 'Buyers', path: 'buyers', icon: <FaUserTie className='mr-3' /> },
{ name: 'Messages', path: 'messages', icon: <FaRegEnvelope className='mr-3' /> },
{ name: 'Users', path: 'users', icon: <FaUsers className='mr-3' /> },
{ name: 'Settings', path: 'settings', icon: <FaCog className='mr-3' /> },
];

Expand Down
8 changes: 4 additions & 4 deletions src/components/chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@ const Chat: React.FC = () => {
</div>

{showChat && (
<div className='fixed inset-0 z-40 flex items-center justify-end mx-2 sm:mr-6'>
<div className='w-9/10 max-w-sm h-3/4 rounded-2xl shadow-xl relative bg-grayColor overflow-hidden '>
<div className='header bg-darkGreen flex justify-between items-center px-6 py-3 text-white'>
<div className='flex items-center gap-4'>
<div className='fixed inset-0 z-40 flex items-center justify-end mx-2 sm:mr-6'>
<div className='w-9/10 max-w-sm h-3/4 rounded-2xl shadow-xl relative bg-grayColor overflow-hidden pt-10'>
<div className='header bg-darkGreen flex justify-between items-center px-6 py-3 text-whiteColor'>
<div className='flex items-center gap-4'>
<img
src={`${profileImage !== null ? profileImage : chatAvatar}`}
alt='Mavericks'
Expand Down
168 changes: 168 additions & 0 deletions src/components/dashboard/BuyersTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import React, { useState, useMemo } from 'react';

interface Column {
key: string;
label: string;
isImage?: boolean;
render?: (item: any) => React.ReactNode;
sortable: boolean;
}

interface TableProps {
data: any[];
columns: Column[];
itemsPerPage: number;
}

const getInitials = (firstName: string, lastName: string) =>
`${firstName.charAt(0)}${lastName.charAt(0)}`.toUpperCase();

const getRandomColor = () => {
const letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};

const Table: React.FC<TableProps> = ({ data, columns, itemsPerPage }) => {
const [currentPage, setCurrentPage] = useState(1);
const [sortColumn, setSortColumn] = useState<string | null>(null);
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc');
const [searchTerm, setSearchTerm] = useState('');

const filteredData = useMemo(
() =>
data.filter(item =>
columns.some(column => String(item[column.key]).toLowerCase().includes(searchTerm.toLowerCase()))
),
[data, columns, searchTerm]
);

const sortedData = useMemo(() => {
if (!sortColumn) return filteredData;
return [...filteredData].sort((a, b) => {
if (a[sortColumn] < b[sortColumn]) return sortDirection === 'asc' ? -1 : 1;
if (a[sortColumn] > b[sortColumn]) return sortDirection === 'asc' ? 1 : -1;
return 0;
});
}, [filteredData, sortColumn, sortDirection]);

const paginatedData = useMemo(() => {
const startIndex = (currentPage - 1) * itemsPerPage;
return sortedData.slice(startIndex, startIndex + itemsPerPage);
}, [sortedData, currentPage, itemsPerPage]);

const totalPages = Math.ceil(sortedData.length / itemsPerPage);

const handleSort = (column: string) => {
if (column === sortColumn) {
setSortDirection(prev => (prev === 'asc' ? 'desc' : 'asc'));
} else {
setSortColumn(column);
setSortDirection('asc');
}
};

const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearchTerm(e.target.value);
setCurrentPage(1);
};

const renderCell = (item: any, column: Column) => {
if (column.isImage) {
return item[column.key] ? (
<img
src={item[column.key]}
alt={`${item.firstName} ${item.lastName}`}
className='ml-2 w-10 h-10 rounded-full object-cover'
/>
) : (
<div
className='ml-2 w-10 h-10 flex items-center justify-center text-whiteColor font-bold text-lg rounded-full uppercase'
style={{ backgroundColor: getRandomColor() }}
>
{getInitials(item.firstName, item.lastName)}
</div>
);
}
if (column.render) return column.render(item);
return column.key.includes('.')
? column.key.split('.').reduce((obj, key) => obj && obj[key], item)
: item[column.key];
};

return (
<div className='overflow-x-auto pl-4'>
<div className='mb-2 flex justify-between items-center'>
<h1 className='text-2xl font-bold'>Buyers Page</h1>
<input
type='text'
placeholder='Search buyer...'
value={searchTerm}
onChange={handleSearch}
className='px-2 py-1 border rounded outline-none'
/>
</div>
<div className='rounded-lg overflow-hidden'>
<table className='min-w-full bg-whiteColor px-10'>
<thead className='bg-darkGreen'>
<tr>
{columns.map(column => (
<th
key={column.key}
onClick={() => column.sortable && handleSort(column.key)}
className={`px-4 py-4 text-sm font-bold text-whiteColor uppercase tracking-wider ${
column.sortable ? 'cursor-pointer' : ''
} ${column.isImage ? 'text-center' : 'text-left'}`}
>
{column.label}
{column.sortable && sortColumn === column.key && <span>{sortDirection === 'asc' ? ' ▲' : ' ▼'}</span>}
</th>
))}
</tr>
</thead>
<tbody>
{paginatedData.map((item, index) => (
<tr key={index} className={index % 2 === 0 ? 'bg-[#F3F4F6]' : 'bg-whiteColor'}>
{columns.map(column => (
<td
key={column.key}
className={`px-4 py-2 whitespace-nowrap text-sm ${column.isImage ? 'text-center' : 'text-left'}`}
>
{renderCell(item, column)}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
<div className='mt-4 flex flex-col sm:flex-row justify-between items-center'>
<div className='mb-2 sm:mb-0 text-sm'>
Showing {(currentPage - 1) * itemsPerPage + 1} to {Math.min(currentPage * itemsPerPage, sortedData.length)} of{' '}
{sortedData.length} buyers
</div>
<div className='flex justify-center'>
<button
onClick={() => setCurrentPage(prev => Math.max(prev - 1, 1))}
disabled={currentPage === 1}
className='px-3 py-1 border rounded-lg border-darkGreen mr-2 text-sm'
>
Previous
</button>
<button
onClick={() => setCurrentPage(prev => Math.min(prev + 1, totalPages))}
disabled={currentPage === totalPages}
className='px-3 py-1 border rounded-lg border-darkGreen text-sm'
>
Next
</button>
</div>
</div>
</div>
);
};

export default Table;
3 changes: 2 additions & 1 deletion src/components/dashboard/UsersTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ const Table: React.FC<TableProps> = ({ data, columns, itemsPerPage }) => {

return (
<div className='overflow-x-auto pl-4'>
<div className='mb-2'>
<div className='mb-2 flex justify-between items-center'>
<h1 className='text-2xl font-bold'>Users Management</h1>
<input
type='text'
placeholder='Search user...'
Expand Down
7 changes: 4 additions & 3 deletions src/components/dashboard/VendorsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ const Table: React.FC<TableProps> = ({ data, columns, itemsPerPage }) => {

return (
<div className='overflow-x-auto pl-4'>
<div className='mb-2'>
<div className='mb-2 flex justify-between items-center'>
<h1 className='text-2xl font-bold'>Sellers Management</h1>
<input
type='text'
placeholder='Search seller...'
Expand All @@ -104,13 +105,13 @@ const Table: React.FC<TableProps> = ({ data, columns, itemsPerPage }) => {
/>
</div>
<table className='min-w-full bg-whiteColor px-10'>
<thead className='bg-[#F9FAFB]'>
<thead className='bg-[#000000a1]'>
<tr>
{columns.map(column => (
<th
key={column.key}
onClick={() => !column.isImage && handleSort(column.key)}
className={`pl-4 py-4 text-left text-sm font-bold text-[#6B7280] uppercase tracking-wider ${!column.isImage ? 'cursor-pointer' : ''}`}
className={`pl-4 py-4 text-left text-sm font-bold text-whiteColor uppercase tracking-wider ${!column.isImage ? 'cursor-pointer' : ''}`}
>
{column.label}
{!column.isImage && sortColumn === column.key && <span>{sortDirection === 'asc' ? ' ▲' : ' ▼'}</span>}
Expand Down
6 changes: 3 additions & 3 deletions src/components/footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function Footer() {
<div className='leading-none text-xs md:text-base break-words flex flex-col gap-3 font-light flex-grow'>
<p>K309 St , Makuza plaza, Nyarugenge , Kigali, Rwanda</p>
<p>[email protected]</p>
<p>+250 788888888</p>
<p>+250 788 888 888</p>
</div>
<div className='flex gap-2'>
<a href='#' target='_blank'>
Expand Down Expand Up @@ -66,12 +66,12 @@ function Footer() {
Join
</button>
</div>
<span className='text-xs text-redColor text-left'>Email is not valid</span>
{/* <span className='text-xs text-redColor text-left'>Email is not valid</span> */}
</form>
</div>
</div>
<p className='p-3 md:p-4 xl:px-10 2xl:w-[1440px] text-xs text-center xl:text-left '>
&copy; 2024 Mavericks Shop. All rights reserved.
&copy; {new Date().getFullYear()} Mavericks Shop. All rights reserved.
</p>
</div>
</>
Expand Down
2 changes: 1 addition & 1 deletion src/components/navbar/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ const Navbar: React.FC = () => {
)}
<div
className={`flex flex-col bg-blackColor md:bg-whiteColor md:text-blackColor text-whiteColor font-roboto w-full 2xl:items-center top-0 ${wish || cartOpen ? 'sticky' : ''
} z-10`}
} sticky z-10`}
>
<div
className='flex justify-between gap-2 flex-wrap p-3 md:p-4 xl:px-10 2xl:w-[1440px] relative'
Expand Down
61 changes: 58 additions & 3 deletions src/pages/admin/Buyers.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,58 @@
export default function Buyers() {
return <div>Buyers</div>;
}
import React, { useEffect } from 'react';
import Table from '../../components/dashboard/BuyersTable';
import { BiLoader } from 'react-icons/bi';
import { useGetBuyersQuery } from '../../services/userApi';
import { User as Buyer } from '../../types/Types';

const Buyers: React.FC = () => {
const {
data: buyersData,
isLoading: buyersLoading,
isError: buyersError,
refetch: refetchBuyers,
} = useGetBuyersQuery(undefined, {
pollingInterval: 30000,
});

const loading = buyersLoading;
const error = buyersError;

const buyers = buyersData?.message || [];

useEffect(() => {
refetchBuyers();
}, [refetchBuyers]);

const columns = [
{ key: 'photoUrl', label: 'Photo', isImage: true, sortable: false },
{ key: 'firstName', label: 'First Name', sortable: true },
{ key: 'lastName', label: 'Last Name', sortable: true },
{ key: 'email', label: 'Email', sortable: true },
{ key: 'phoneNumber', label: 'Phone', sortable: true },
{ key: 'gender', label: 'Gender', sortable: true },
{
key: 'Role.name',
label: 'Role',
render: (buyer: Buyer) => buyer.Role.name.toUpperCase(),
sortable: false,
},
];

if (loading)
return (
<div className='text-center content-center h-screen py-4'>
<BiLoader className='animate-spin w-full h-full max-h-12 text-[#3B82F6]' />
</div>
);
if (error) {
return <div className='text-center py-4 text-redColor'>Error fetching data. Please try again.</div>;
}

return (
<div className='container mx-auto p-4 lg:pl-56'>
<Table data={buyers} columns={columns} itemsPerPage={10} />
</div>
);
};

export default Buyers;
3 changes: 0 additions & 3 deletions src/pages/admin/Messages.tsx

This file was deleted.

Loading

0 comments on commit 7104c0f

Please sign in to comment.