219 lines
8.2 KiB
TypeScript
219 lines
8.2 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { FaCheck } from "react-icons/fa";
|
|
import { PiDotsThreeOutlineVerticalFill } from "react-icons/pi";
|
|
import { FaDatabase } from "react-icons/fa";
|
|
import AccountNav from "./AccountNav";
|
|
import api from "../../config/api";
|
|
import { AccountData, AccountStatus, UserRole } from "../../utlity/types";
|
|
import { useNavigate } from "react-router-dom";
|
|
import Cards from "../home/Cards";
|
|
import AddAccountModal from "./AddAccountModal";
|
|
import { Bounce, toast } from "react-toastify";
|
|
import Loader from "../../helper/Loader";
|
|
|
|
// 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 [selectedAccounts, setSelectedAccounts] = useState<number[]>([]);
|
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
const [accounts, setAccounts] = useState<string[]>([]);
|
|
const [openMenuId, setOpenMenuId] = useState<string | null>(null);
|
|
|
|
const handleOpenModal = () => {
|
|
setIsModalOpen(true);
|
|
};
|
|
|
|
const navigate = useNavigate();
|
|
|
|
const fetchAccounts = async () => {
|
|
try {
|
|
const response = await api("/api/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/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/accounts/${accountId}/accept/invite`)
|
|
console.log(response)
|
|
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 (
|
|
<>
|
|
<Cards type="account" onOpenModal={handleOpenModal} />
|
|
{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;
|