Edit Signature and Account Card , Trans
This commit is contained in:
parent
da37cbe16a
commit
2bb54b2043
|
|
@ -4,6 +4,8 @@ import AccountNav from "./AccountNav";
|
||||||
import api from "../../config/api";
|
import api from "../../config/api";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { FaCheck } from "react-icons/fa";
|
import { FaCheck } from "react-icons/fa";
|
||||||
|
import Cards from "../home/Cards";
|
||||||
|
import AddInviteModal from "./AddInviteModal";
|
||||||
|
|
||||||
interface Transaction {
|
interface Transaction {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
@ -23,6 +25,12 @@ const Account = () => {
|
||||||
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
||||||
const [selectedTransactions, setSelectedTransactions] = useState<number[]>([]);
|
const [selectedTransactions, setSelectedTransactions] = useState<number[]>([]);
|
||||||
|
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
|
||||||
|
const handleOpenModal = () => {
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
const fetchTransactions = async () => {
|
const fetchTransactions = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await api(`/api/accounts/${accountId}/transactions`);
|
const response = await api(`/api/accounts/${accountId}/transactions`);
|
||||||
|
|
@ -42,8 +50,14 @@ const Account = () => {
|
||||||
navigate(`/accounts/${accountId}/transactions/${id}`);
|
navigate(`/accounts/${accountId}/transactions/${id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAddAccount = (accountName: string) => {
|
||||||
|
console.log(accountId)
|
||||||
|
setIsModalOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<Cards type="account" onOpenModal={handleOpenModal} />
|
||||||
{transactions.length > 0 && (
|
{transactions.length > 0 && (
|
||||||
<div className="overflow-x-auto py-4">
|
<div className="overflow-x-auto py-4">
|
||||||
<table className="min-w-full text-sm border border-gray-200 rounded bg-white shadow-sm">
|
<table className="min-w-full text-sm border border-gray-200 rounded bg-white shadow-sm">
|
||||||
|
|
@ -102,6 +116,8 @@ const Account = () => {
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{isModalOpen && <AddInviteModal onClose={() => setIsModalOpen(false)}
|
||||||
|
onAddAccount={handleAddAccount} />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,64 +1,63 @@
|
||||||
import React, { use, useEffect } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
// import { useSelector } from 'react-redux';
|
||||||
import { Link, useLocation, useParams } from 'react-router-dom';
|
import { Link, useLocation } from 'react-router-dom';
|
||||||
import { FaCog } from 'react-icons/fa';
|
import { FaSignOutAlt,FaUser } from "react-icons/fa";
|
||||||
import { IoIosNotifications } from 'react-icons/io';
|
|
||||||
import { RootState } from '../../store/store';
|
|
||||||
import { UserState } from '../../store/slices/userSlice';
|
|
||||||
import api from '../../config/api';
|
import api from '../../config/api';
|
||||||
|
|
||||||
// const fetchAccountData = async (accountId: string) => {
|
// import { RootState } from '../../store/store';
|
||||||
// try {
|
// import { UserState } from '../../store/slices/userSlice';
|
||||||
// if(!accountId) return null;
|
|
||||||
// const response = await api.get(`/api/accounts/${accountId}`);
|
|
||||||
// return response.data;
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error("Error fetching account data:", error);
|
|
||||||
// throw error;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const fetchTransactionData = async (accountId: string, transactionId: string) => {
|
|
||||||
// try {
|
|
||||||
// if(!accountId || !transactionId) return null;
|
|
||||||
// const response = await api.get(`/api/accounts/${accountId}/transactions/${transactionId}`);
|
|
||||||
// return response.data;
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error("Error fetching account data:", error);
|
|
||||||
// throw error;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
interface AccountNavProps {
|
interface AccountNavProps {
|
||||||
accountName: string | null;
|
accountName?: string | null;
|
||||||
transactionName: string | null;
|
transactionName?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define the shape of state object
|
||||||
|
interface Account {
|
||||||
|
id: number;
|
||||||
|
isDefault: boolean;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserData {
|
||||||
|
id: string;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
email: string;
|
||||||
|
language: string;
|
||||||
|
account: Account;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AccountNav: React.FC<AccountNavProps> = ({ accountName = null, transactionName = null }) => {
|
const AccountNav: React.FC<AccountNavProps> = ({ accountName = null, transactionName = null }) => {
|
||||||
const user: UserState = useSelector((state: RootState) => state.user);
|
// const user: UserState = useSelector((state: RootState) => state.user);
|
||||||
|
|
||||||
const initials = user.username
|
const [user, setUser] = useState<UserData>({
|
||||||
? user.username
|
id: "",
|
||||||
.split(' ')
|
firstName: "",
|
||||||
.map((n) => n[0])
|
lastName: "",
|
||||||
.join('')
|
email: "",
|
||||||
.slice(0, 2)
|
language: "",
|
||||||
.toUpperCase()
|
account: {
|
||||||
: 'U';
|
id: 0,
|
||||||
|
isDefault: false,
|
||||||
|
name: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const getUserDetails = async () => {
|
||||||
|
try {
|
||||||
|
let res = await api.get(`/api/user/details`)
|
||||||
|
console.log("getuser", res)
|
||||||
|
setUser(res?.data)
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching user details:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getUserDetails()
|
||||||
|
}, [])
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// if (accountId) {
|
|
||||||
// api.get(`/api/accounts/${accountId}`)
|
|
||||||
// .then(response => {
|
|
||||||
// // Handle the account data if needed
|
|
||||||
// console.log(response.data, "Line 29");
|
|
||||||
// })
|
|
||||||
// .catch(error => {
|
|
||||||
// console.error("Error fetching account data:", error);
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }, [accountId, transactionId]);
|
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
|
|
@ -66,74 +65,121 @@ const AccountNav: React.FC<AccountNavProps> = ({ accountName = null, transaction
|
||||||
.split("/")
|
.split("/")
|
||||||
.filter((x) => x && isNaN(Number(x))); // remove numeric IDs
|
.filter((x) => x && isNaN(Number(x))); // remove numeric IDs
|
||||||
|
|
||||||
const labelMap = {
|
const labelMap: Record<string, string> = {
|
||||||
accounts: "Accounts",
|
accounts: "Accounts",
|
||||||
transactions: "Transactions",
|
transactions: "Transactions",
|
||||||
// Add more routes here if needed
|
// Add more routes here if needed
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
// Close dropdown when clicked outside
|
||||||
|
useEffect(() => {
|
||||||
|
function handleClickOutside(event: MouseEvent) {
|
||||||
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("mousedown", handleClickOutside);
|
||||||
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
// Add logout logic here
|
||||||
|
console.log('Logging out...');
|
||||||
|
setOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="w-full px-6 py-2 flex justify-between items-center bg-white border-b rounded-sm border-gray-200">
|
<header className="w-full px-6 py-2 flex justify-between items-center bg-white border-b rounded-sm border-gray-200">
|
||||||
|
<nav className="text-md font-medium text-gray-800 capitalize">
|
||||||
|
{pathnames.map((segment, index) => {
|
||||||
|
const label = labelMap[segment] || segment;
|
||||||
|
const to = "/" + pathnames.slice(0, index + 1).join("/");
|
||||||
|
const isLast = index === pathnames.length - 1;
|
||||||
|
|
||||||
<nav className="text-md font-medium text-gray-800 capitalize">
|
return (
|
||||||
{pathnames.map((segment, index) => {
|
<span key={to}>
|
||||||
const label = labelMap[segment] || segment;
|
{!isLast ? (
|
||||||
const to = "/" + pathnames.slice(0, index + 1).join("/");
|
<>
|
||||||
|
<Link
|
||||||
|
to={to}
|
||||||
|
className="text-blue-600 hover:text-blue-700 hover:underline transition-colors"
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Link>
|
||||||
|
<span className="mx-2 text-gray-400">{">"}</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<span className="text-gray-900 font-semibold">{label}</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
|
|
||||||
const isLast = index === pathnames.length - 1;
|
<div className="flex items-center space-x-2">
|
||||||
|
|
||||||
return (
|
{/* User Dropdown */}
|
||||||
<span key={to}>
|
<div className="relative" ref={dropdownRef}>
|
||||||
{!isLast ? (
|
{/* Avatar Button */}
|
||||||
<>
|
|
||||||
<Link
|
|
||||||
to={to}
|
|
||||||
className="text-blue-600 hover:underline"
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
</Link>
|
|
||||||
<span className="mx-1">{">"}</span>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<span>{label}</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
{/* <h1 className="text-md font-medium text-gray-800 capitalize">Account</h1> */}
|
|
||||||
|
|
||||||
<div className="flex items-center">
|
|
||||||
{/* Notification + Settings with smaller gap */}
|
|
||||||
<div className="flex items-center">
|
|
||||||
<button
|
<button
|
||||||
aria-label="Notifications"
|
className="flex items-center space-x-2 p-1 cursor-pointer rounded-lg transition-colors"
|
||||||
className="p-2 rounded-full hover:bg-gray-100 transition-colors text-gray-600"
|
onClick={() => setOpen(!open)}
|
||||||
|
aria-expanded={open}
|
||||||
|
aria-haspopup="true"
|
||||||
>
|
>
|
||||||
<IoIosNotifications className="text-xl" />
|
<div className="w-8 h-8 rounded-full overflow-hidden bg-gradient-to-br from-green-500 to-blue-600 flex items-center justify-center text-white font-semibold text-sm shadow-md">
|
||||||
|
{user ? (
|
||||||
|
<span
|
||||||
|
className="w-full h-full object-cover p-1.5 font-bold"
|
||||||
|
>{(user.firstName.slice(0,1) + user.lastName.slice(0,1)).toUpperCase()}</span>
|
||||||
|
) : (
|
||||||
|
<FaUser className="text-lg" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Link
|
{/* Enhanced Dropdown */}
|
||||||
to="/s/settings"
|
{open && (
|
||||||
aria-label="Settings"
|
<div className="absolute right-0 mt-2 w-72 top-8 rounded-xl shadow-xl bg-white border border-gray-200 z-50 overflow-hidden">
|
||||||
className="p-2 rounded-full hover:bg-gray-100 transition-colors text-gray-600"
|
{/* User Info Section */}
|
||||||
>
|
<div className="px-4 py-4 bg-gradient-to-r from-blue-50 to-purple-50 border-b border-gray-100">
|
||||||
<FaCog className="text-sm" />
|
<div className="flex items-center space-x-3">
|
||||||
</Link>
|
<div className="w-12 h-12 rounded-full overflow-hidden bg-gradient-to-br from-green-500 to-blue-600 flex items-center justify-center text-white font-semibold shadow-md">
|
||||||
</div>
|
{(user) ? (
|
||||||
|
<span
|
||||||
|
className="w-full h-full object-cover px-3 text-xl py-2.5 font-bold"
|
||||||
|
>{(user.firstName.slice(0,1) + user.lastName.slice(0,1)).toUpperCase()}</span>
|
||||||
|
) : (
|
||||||
|
<FaUser className="text-lg" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="font-semibold text-gray-900 truncate">{user.firstName + ' ' +user.lastName || '-'}</div>
|
||||||
|
<div className="text-sm text-gray-600 truncate">{user.email || '-'}</div>
|
||||||
|
<div className="text-xs text-gray-500 mt-1">
|
||||||
|
Account #{user?.account?.name || '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Avatar with separate spacing */}
|
{/* Logout Section */}
|
||||||
<div className="w-9 h-9 ms-3 rounded-full overflow-hidden bg-[var(--primary)] flex items-center justify-center text-white font-semibold text-sm">
|
<div className="border-t border-gray-100 ">
|
||||||
{user.picture ? (
|
<button
|
||||||
<img
|
onClick={handleLogout}
|
||||||
src={user.picture}
|
className="w-full flex items-center px-4 py-3 text-sm text-red-600 hover:bg-red-50 transition-colors cursor-pointer"
|
||||||
alt="User avatar"
|
>
|
||||||
className="w-full h-full object-cover"
|
<FaSignOutAlt className="mr-3" />
|
||||||
/>
|
<span>Log Out</span>
|
||||||
) : (
|
</button>
|
||||||
initials
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -141,4 +187,4 @@ const AccountNav: React.FC<AccountNavProps> = ({ accountName = null, transaction
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AccountNav;
|
export default AccountNav;
|
||||||
|
|
@ -6,6 +6,9 @@ import AccountNav from "./AccountNav";
|
||||||
import api from "../../config/api";
|
import api from "../../config/api";
|
||||||
import { AccountData, AccountStatus, UserRole } from "../../utlity/types";
|
import { AccountData, AccountStatus, UserRole } from "../../utlity/types";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import Cards from "../home/Cards";
|
||||||
|
import AddAccountModal from "./AddAccountModal";
|
||||||
|
import { Bounce, toast } from "react-toastify";
|
||||||
|
|
||||||
// import { UserRole, AccountStatus, AccountData } from "../../types/account";
|
// import { UserRole, AccountStatus, AccountData } from "../../types/account";
|
||||||
|
|
||||||
|
|
@ -32,6 +35,13 @@ const getStatusLabel = (
|
||||||
const Accounts = () => {
|
const Accounts = () => {
|
||||||
const [allAccounts, setAllAccounts] = useState<AccountData[]>([]);
|
const [allAccounts, setAllAccounts] = useState<AccountData[]>([]);
|
||||||
const [selectedAccounts, setSelectedAccounts] = useState<number[]>([]);
|
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 navigate = useNavigate();
|
||||||
|
|
||||||
|
|
@ -53,8 +63,43 @@ const Accounts = () => {
|
||||||
navigate(`/accounts/${id}/transactions`);
|
navigate(`/accounts/${id}/transactions`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAddAccount = (accountName: string) => {
|
||||||
|
setAccounts((prev) => [...prev, accountName]);
|
||||||
|
setIsModalOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<Cards type="account" onOpenModal={handleOpenModal} />
|
||||||
{allAccounts.length > 0 && (
|
{allAccounts.length > 0 && (
|
||||||
<div className="overflow-x-auto py-2">
|
<div className="overflow-x-auto py-2">
|
||||||
<table className="min-w-full text-sm border border-gray-200 rounded bg-white shadow-sm">
|
<table className="min-w-full text-sm border border-gray-200 rounded bg-white shadow-sm">
|
||||||
|
|
@ -101,8 +146,38 @@ const Accounts = () => {
|
||||||
{status.text}
|
{status.text}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3 text-center">
|
<td className="px-4 py-3 text-center relative">
|
||||||
<PiDotsThreeOutlineVerticalFill className="text-lg text-slate-300 hover:text-black mx-auto" />
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
|
@ -111,6 +186,8 @@ const Accounts = () => {
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{isModalOpen && <AddAccountModal onClose={() => setIsModalOpen(false)}
|
||||||
|
onAddAccount={handleAddAccount} />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
interface AddAccountModalProps {
|
||||||
|
onClose: () => void;
|
||||||
|
onAddAccount: (accountName: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AddAccountModal: React.FC<AddAccountModalProps> = ({ onClose, onAddAccount }) => {
|
||||||
|
const [inputValue, setInputValue] = useState('');
|
||||||
|
|
||||||
|
const handleAddClick = () => {
|
||||||
|
if (inputValue.trim()) {
|
||||||
|
onAddAccount(inputValue.trim());
|
||||||
|
setInputValue(''); // clear input after submit
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 backdrop-blur-sm flex items-center justify-center z-50">
|
||||||
|
<div className="bg-white rounded-lg shadow-xl p-6 w-80">
|
||||||
|
<h3 className="text-lg font-medium text-gray-800 mb-4">Add New Account</h3>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter account name"
|
||||||
|
value={inputValue}
|
||||||
|
onChange={(e) => setInputValue(e.target.value)}
|
||||||
|
className="w-full mb-4 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[var(--primary)]"
|
||||||
|
/>
|
||||||
|
<div className="flex justify-end gap-3">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setInputValue('');
|
||||||
|
onClose();
|
||||||
|
}}
|
||||||
|
className="px-4 py-1.5 text-[13px] cursor-pointer font-semibold text-gray-600 hover:bg-gray-100 rounded-md transition-colors"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleAddClick}
|
||||||
|
className="px-2 py-1.5 cursor-pointer font-semibold bg-[var(--modalSubmitBG)] text-[13px] text-white hover:bg-[var(--modalSubmitBGHover)] rounded-md transition-colors"
|
||||||
|
disabled={!inputValue.trim()}
|
||||||
|
>
|
||||||
|
Add Account
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddAccountModal;
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
interface AddAccountModalProps {
|
||||||
|
onClose: () => void;
|
||||||
|
onAddAccount: (accountName: string) => void;
|
||||||
|
}
|
||||||
|
const AddInviteModal : React.FC<AddAccountModalProps> = ({ onClose, onAddAccount }) => {
|
||||||
|
const [inputValue, setInputValue] = useState('');
|
||||||
|
|
||||||
|
const handleAddClick = () => {
|
||||||
|
if (inputValue.trim()) {
|
||||||
|
onAddAccount(inputValue.trim());
|
||||||
|
setInputValue(''); // clear input after submit
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 backdrop-blur-sm flex items-center justify-center z-50">
|
||||||
|
<div className="bg-white rounded-lg shadow-xl p-6 w-80">
|
||||||
|
<h3 className="text-lg font-medium text-gray-800 mb-4">Send Invite</h3>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter account name"
|
||||||
|
value={inputValue}
|
||||||
|
onChange={(e) => setInputValue(e.target.value)}
|
||||||
|
className="w-full mb-4 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[var(--primary)]"
|
||||||
|
/>
|
||||||
|
<div className="flex justify-end gap-3">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setInputValue('');
|
||||||
|
onClose();
|
||||||
|
}}
|
||||||
|
className="px-4 py-1.5 text-[13px] cursor-pointer font-semibold text-gray-600 hover:bg-gray-100 rounded-md transition-colors"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleAddClick}
|
||||||
|
className="px-2 py-1.5 cursor-pointer font-semibold bg-[var(--modalSubmitBG)] text-[13px] text-white hover:bg-[var(--modalSubmitBGHover)] rounded-md transition-colors"
|
||||||
|
disabled={!inputValue.trim()}
|
||||||
|
>
|
||||||
|
Add Account
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AddInviteModal
|
||||||
|
|
@ -1,7 +1,22 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { FaCheckCircle, FaClock, FaHourglassHalf, FaTasks } from 'react-icons/fa';
|
import { FaCheckCircle, FaClock, FaHourglassHalf, FaTasks } from 'react-icons/fa';
|
||||||
|
|
||||||
const cards = [
|
// Define a type for card data
|
||||||
|
interface CardData {
|
||||||
|
title: string;
|
||||||
|
count: number;
|
||||||
|
icon: JSX.Element;
|
||||||
|
ring: string;
|
||||||
|
bg: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CardsProps {
|
||||||
|
type?: 'dashboard';
|
||||||
|
// onCardClick?: (card: CardData) => void;
|
||||||
|
onOpenModal?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cards: CardData[] = [
|
||||||
{
|
{
|
||||||
title: 'Action Required',
|
title: 'Action Required',
|
||||||
count: 12,
|
count: 12,
|
||||||
|
|
@ -32,48 +47,89 @@ const cards = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const AccountData: CardData[] = [
|
||||||
|
{
|
||||||
|
title: 'Transaction',
|
||||||
|
count: 12,
|
||||||
|
icon: <FaTasks size={20} className="text-orange-500" />,
|
||||||
|
ring: 'ring-orange-400/20',
|
||||||
|
bg: 'bg-orange-50',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'In-Progress',
|
||||||
|
count: 8,
|
||||||
|
icon: <FaClock size={20} className="text-yellow-500" />,
|
||||||
|
ring: 'ring-yellow-400/20',
|
||||||
|
bg: 'bg-yellow-50',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Deactive',
|
||||||
|
count: 5,
|
||||||
|
icon: <FaHourglassHalf size={20} className="text-red-500" />,
|
||||||
|
ring: 'ring-red-400/20',
|
||||||
|
bg: 'bg-red-50',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Completed',
|
||||||
|
count: 27,
|
||||||
|
icon: <FaCheckCircle size={20} className="text-green-500" />,
|
||||||
|
ring: 'ring-green-400/20',
|
||||||
|
bg: 'bg-green-50',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const filters = ['Last 7 Days', 'Last 30 Days', 'Last 6 Months', 'This Year'];
|
const filters = ['Last 7 Days', 'Last 30 Days', 'Last 6 Months', 'This Year'];
|
||||||
|
|
||||||
const Cards = () => {
|
const Cards: React.FC<CardsProps> = ({ type, onOpenModal }) => {
|
||||||
const [selectedFilter, setSelectedFilter] = useState('Last 7 Days');
|
const [selectedFilter, setSelectedFilter] = useState<string>('Last 7 Days');
|
||||||
|
const [showCards, setShowCards] = useState<CardData[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (type === 'account') {
|
||||||
|
setShowCards(AccountData);
|
||||||
|
} else {
|
||||||
|
setShowCards(cards);
|
||||||
|
}
|
||||||
|
}, [type]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full px-4 py-3">
|
<div className="w-full px-4 py-3">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex flex-col md:flex-row items-start md:items-center justify-between mb-5 gap-3">
|
<div className="flex flex-col md:flex-row items-start md:items-center justify-between mb-5 gap-3">
|
||||||
<h2 className="text-lg font-medium text-gray-700">Overview</h2>
|
<h2 className="text-lg font-medium text-gray-700">Overview</h2>
|
||||||
|
|
||||||
<div className="flex gap-2 w-full md:w-auto">
|
<div className="flex gap-2 w-full md:w-auto">
|
||||||
{/* Account Selector */}
|
{/* Account Selector */}
|
||||||
<select
|
<select
|
||||||
value={'All'}
|
value={'All'}
|
||||||
onChange={() => {}}
|
onChange={() => { }}
|
||||||
className="text-sm text-gray-700 border border-gray-300 rounded-sm px-3 py-1.5 focus:outline-none bg-white shadow-sm"
|
className="text-sm text-gray-700 border border-gray-300 rounded-sm px-3 py-1.5 focus:outline-none bg-white shadow-sm"
|
||||||
>
|
>
|
||||||
<option>All</option>
|
<option>All</option>
|
||||||
<option>Account 1</option>
|
<option>Account 1</option>
|
||||||
<option>Account 2</option>
|
<option>Account 2</option>
|
||||||
<option>Account 3</option>
|
<option>Account 3</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
{/* Date Filter */}
|
|
||||||
<select
|
|
||||||
value={selectedFilter}
|
|
||||||
onChange={(e) => setSelectedFilter(e.target.value)}
|
|
||||||
className="text-sm text-gray-700 border border-gray-300 rounded-sm px-3 py-1.5 focus:outline-none bg-white shadow-sm"
|
|
||||||
>
|
|
||||||
{filters.map((f) => (
|
|
||||||
<option key={f}>{f}</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
{/* Date Filter */}
|
||||||
|
<select
|
||||||
|
value={selectedFilter}
|
||||||
|
onChange={(e) => setSelectedFilter(e.target.value)}
|
||||||
|
className="text-sm text-gray-700 border border-gray-300 rounded-sm px-3 py-1.5 focus:outline-none bg-white shadow-sm"
|
||||||
|
>
|
||||||
|
{filters.map((f) => (
|
||||||
|
<option key={f}>{f}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
{type === 'account' &&
|
||||||
|
<button className="px-2 py-1.5 cursor-pointer font-semibold bg-[var(--primary)] text-[13px] text-white hover:bg-[var(--primaryLight2)] rounded-md transition-colors" onClick={onOpenModal}>Add Account</button>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Cards */}
|
{/* Cards */}
|
||||||
<div className="cursor-pointer grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
<div className="cursor-pointer grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
{cards.map((card) => (
|
{showCards.map((card) => (
|
||||||
<div
|
<div
|
||||||
key={card.title}
|
key={card.title}
|
||||||
className={`rounded-md bg-white/60 backdrop-blur-md border border-gray-200 p-5 transition-transform shadow-md hover:scale-[1.02] hover:shadow-lg`}
|
className={`rounded-md bg-white/60 backdrop-blur-md border border-gray-200 p-5 transition-transform shadow-md hover:scale-[1.02] hover:shadow-lg`}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,9 @@
|
||||||
--hideModalBg: #2d2d2d;
|
--hideModalBg: #2d2d2d;
|
||||||
|
|
||||||
--processStepOneTextHead: #000000;
|
--processStepOneTextHead: #000000;
|
||||||
|
|
||||||
|
--modalSubmitBG:#1919d7;
|
||||||
|
--modalSubmitBGHover:#395ee1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue