SME-Web/src/components/accounts/Accounts.tsx

208 lines
7.8 KiB
TypeScript

import { useEffect, useState } from "react";
import { PiDotsThreeOutlineVerticalFill } from "react-icons/pi";
import { FaDatabase } from "react-icons/fa";
import api from "../../config/api";
import { AccountData, AccountStatus, UserRole } from "../../utlity/types";
import { useNavigate } from "react-router-dom";
import AddAccountModal from "./AddAccountModal";
import { Bounce, toast } from "react-toastify";
// import { UserRole, AccountStatus, AccountData } from "../../types/account";
const roleColors: Record<UserRole, string> = {
[UserRole.OWNER]: "bg-purple-100 text-purple-700",
[UserRole.ADMIN]: "bg-blue-100 text-blue-700",
[UserRole.EDITOR]: "bg-green-100 text-green-700",
[UserRole.VIEWER]: "bg-gray-100 text-gray-600",
};
const getStatusLabel = (
status: AccountStatus,
role: UserRole
): { text: string; color: string } => {
if (status == AccountStatus.ACTIVE && role != UserRole.OWNER)
return { text: "Accepted", color: "text-green-600 bg-green-100" };
if (status == AccountStatus.REJECTED)
return { text: "Rejected", color: "text-red-600 bg-red-100" };
if (status == AccountStatus.INVITED)
return { text: "Pending", color: "text-yellow-600 bg-yellow-100" };
return { text: "Active", color: "text-blue-600 bg-blue-100" };
};
const Accounts = () => {
const [allAccounts, setAllAccounts] = useState<AccountData[]>([]);
const [isModalOpen, setIsModalOpen] = useState(false);
const [openMenuId, setOpenMenuId] = useState<string | null>(null);
const navigate = useNavigate();
const fetchAccounts = async () => {
try {
const response = await api("/api/v1/user/accounts");
const data: AccountData[] = response.data || [];
setAllAccounts(data);
} catch (error) {
console.error("Error fetching accounts:", error);
}
};
useEffect(() => {
fetchAccounts();
}, []);
const handleSelect = (id: number) => {
navigate(`/accounts/${id}/transactions`);
};
const handleAddAccount = async (accountName: string) => {
try {
const payloadAccount = {
name: accountName
}
let res = await api.post(`/api/v1/user/accounts`, payloadAccount)
if (res?.data) {
// toast.success("Account Add Successfully...!", {
// position: "top-center",
// autoClose: 5000,
// hideProgressBar: false,
// closeOnClick: false,
// pauseOnHover: true,
// draggable: true,
// progress: undefined,
// theme: "light",
// transition: Bounce,
// });
await fetchAccounts()
setIsModalOpen(false);
}
} catch (error) {
console.log(error)
} finally {
}
};
const handleMenuToggle = (accountId: string) => {
setOpenMenuId(openMenuId == accountId ? null : accountId);
};
const handleAction = async (accountId: string, action: 'accept' | 'reject') => {
try {
let response = await api.post(`/api/v1/accounts/${accountId}/accept/invite`)
if (response.data.status == 202) {
toast.success("Login Successful", {
position: "top-center",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: false,
pauseOnHover: true,
draggable: true,
progress: undefined,
theme: "light",
transition: Bounce,
});
}
} catch (error) {
console.error('Error posting data:', error);
} finally {
setOpenMenuId(null); // close menu
}
};
return (
<>
{allAccounts.length > 0 && (
<div className="overflow-x-auto py-2">
<table className="min-w-full text-sm border border-gray-200 rounded bg-white shadow-sm">
<thead className="bg-gray-100 text-gray-700 uppercase text-xs tracking-wider">
<tr>
{/* <th className="px-4 py-3 w-12"></th> */}
<th className="px-4 py-3 text-left">Account Name</th>
<th className="px-4 py-3 text-center">Account ID</th>
<th className="px-4 py-3 text-center">Role</th>
<th className="px-4 py-3 text-center">Status</th>
<th className="px-4 py-3 text-center"></th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200">
{allAccounts.map((account) => {
const status = getStatusLabel(account.status, account.role);
return (
<tr
key={account.accountId}
onClick={() => handleSelect(account.accountId)}
className="hover:bg-gray-50 cursor-pointer"
>
{/* <td className="px-4 py-3">
<div className="w-5 h-5 rounded border border-slate-300 flex justify-center items-center">
{selectedAccounts.includes(account.accountId) && (
<FaCheck className="text-purple-600 text-sm" />
)}
</div>
</td> */}
<td className="px-4 py-3">
<div className="flex items-center gap-2">
<FaDatabase className="text-[var(--primary)]" />
<span>{account.accountName}</span>
</div>
</td>
<td className="px-4 py-3 text-center">{account.accountId}</td>
<td className="px-4 py-3 text-center">
<span className={`px-2 py-0.5 rounded-xl text-xs font-medium ${roleColors[account.role]}`}>
{account.role}
</span>
</td>
<td className="px-4 py-3 text-center">
<span className={`px-2 py-0.5 rounded-xl text-xs font-medium ${status.color}`}>
{status.text}
</span>
</td>
<td className="px-4 py-3 text-center relative">
<button
onClick={(e) => {
e.stopPropagation();
handleMenuToggle(account.accountId);
}}
className="mx-auto"
aria-label="Actions menu"
>
<PiDotsThreeOutlineVerticalFill className="text-lg text-slate-300 hover:text-black" />
</button>
{/* Dropdown menu */}
{openMenuId == account.accountId && (
<div
className="absolute right-13 top-6 mt-1 w-18 bg-white border text-[13px] font-bold border-gray-300 rounded-md shadow-lg z-10"
onClick={(e) => e.stopPropagation()} // prevent row click closing
>
<button
className="w-full px-3 py-1 text-left text-green-700 hover:bg-green-100 rounded-t-md cursor-pointer"
onClick={() => handleAction(account.accountId, 'accept')}
>
Accept
</button>
<button
className="w-full px-3 py-1 text-left text-red-700 hover:bg-red-100 rounded-b-md cursor-pointer"
onClick={() => handleAction(account.accountId, 'reject')}
>
Reject
</button>
</div>
)}
</td>
</tr>
);
})}
</tbody>
</table>
</div>
)}
{isModalOpen && <AddAccountModal onClose={() => setIsModalOpen(false)}
onAddAccount={handleAddAccount} />}
</>
);
};
export default Accounts;