jai #4
|
|
@ -110,6 +110,12 @@ const Account = () => {
|
||||||
// Add more cards here if needed
|
// Add more cards here if needed
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [activeAccountId, setActiveAccountId] = useState<string | null>(
|
||||||
|
JSON.parse(sessionStorage.getItem('activeAccountId') || 'null')
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("activeAccountId Trans.",activeAccountId)
|
||||||
// const { accountId } = useParams<{ accountId: string }>();
|
// const { accountId } = useParams<{ accountId: string }>();
|
||||||
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
||||||
const [selectedTransactions, setSelectedTransactions] = useState<number[]>([]);
|
const [selectedTransactions, setSelectedTransactions] = useState<number[]>([]);
|
||||||
|
|
@ -121,25 +127,52 @@ const Account = () => {
|
||||||
|
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
|
||||||
const getUserDetails = async () => {
|
// const getUserDetails = async () => {
|
||||||
try {
|
// try {
|
||||||
let res = await api.get('/api/user/details')
|
// let res = await api.get('/api/user/details')
|
||||||
let accountIDData = res?.data?.account?.id
|
// let accountIDData = res?.data?.account?.id
|
||||||
setAccountID(accountIDData)
|
// setAccountID(accountIDData)
|
||||||
} catch (error) {
|
// } catch (error) {
|
||||||
console.log(error)
|
// console.log(error)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// getUserDetails()
|
||||||
|
// }, [])
|
||||||
|
|
||||||
|
// Listen for sessionStorage changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getUserDetails()
|
const handleStorageChange = (e: StorageEvent) => {
|
||||||
}, [])
|
if (e.key === 'activeAccountId') {
|
||||||
|
const newActiveAccountId = e.newValue ? JSON.parse(e.newValue) : null;
|
||||||
|
setActiveAccountId(newActiveAccountId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Listen for storage events (works for changes from other tabs)
|
||||||
|
window.addEventListener('storage', handleStorageChange);
|
||||||
|
|
||||||
|
// Custom event listener for same-tab changes
|
||||||
|
const handleCustomStorageChange = (e: CustomEvent) => {
|
||||||
|
if (e.detail.key === 'activeAccountId') {
|
||||||
|
setActiveAccountId(e.detail.newValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('sessionStorageChange', handleCustomStorageChange as EventListener);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('storage', handleStorageChange);
|
||||||
|
window.removeEventListener('sessionStorageChange', handleCustomStorageChange as EventListener);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
const getDashboardData = async () => {
|
const getDashboardData = async () => {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
const payload = {
|
const payload = {
|
||||||
account: accountID
|
account: activeAccountId
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -154,50 +187,51 @@ const Account = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (accountID) {
|
|
||||||
getDashboardData()
|
|
||||||
fetchTransactions();
|
|
||||||
}
|
|
||||||
}, [accountID]);
|
|
||||||
|
|
||||||
const handleOpenModal = () => {
|
|
||||||
setIsModalOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchTransactions = async () => {
|
const fetchTransactions = async () => {
|
||||||
console.log("fetchTransactions")
|
console.log("fetchTransactions")
|
||||||
try {
|
try {
|
||||||
const response = await api(`/api/accounts/${accountID}/transactions`);
|
const response = await api(`/api/accounts/${activeAccountId}/transactions`);
|
||||||
setTransactions(response.data || []);
|
setTransactions(response.data || []);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching transactions:", error);
|
console.error("Error fetching transactions:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeAccountId) {
|
||||||
|
getDashboardData()
|
||||||
|
fetchTransactions();
|
||||||
|
}
|
||||||
|
}, [activeAccountId]);
|
||||||
|
|
||||||
|
const handleOpenModal = () => {
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// if (accountID) fetchTransactions();
|
// if (accountID) fetchTransactions();
|
||||||
// }, [accountID]);
|
// }, [accountID]);
|
||||||
|
|
||||||
const getAccountDetails = async () => {
|
// const getAccountDetails = async () => {
|
||||||
try {
|
// try {
|
||||||
let res = await api.get('/api/user/accounts')
|
// let res = await api.get('/api/user/accounts')
|
||||||
let items = res?.data
|
// let items = res?.data
|
||||||
setAccountsData(items)
|
// setAccountsData(items)
|
||||||
} catch (error) {
|
// } catch (error) {
|
||||||
console.log(error)
|
// console.log(error)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
getAccountDetails()
|
// getAccountDetails()
|
||||||
}, [])
|
// }, [])
|
||||||
|
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const handleTransactionClick = (id: number) => {
|
const handleTransactionClick = (id: number) => {
|
||||||
navigate(`/accounts/${accountID}/transactions/${id}`);
|
navigate(`/accounts/${activeAccountId}/transactions/${id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleInvite = async (data: { i_accountName: number; email: string; role: string }) => {
|
const handleInvite = async (data: { i_accountName: number; email: string; role: string }) => {
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,13 @@
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
// import { useSelector } from 'react-redux';
|
|
||||||
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
||||||
import { FaSignOutAlt, FaUser, FaBuilding, FaChevronDown, FaCheck } from "react-icons/fa";
|
import { FaSignOutAlt, FaUser, FaBuilding, FaChevronDown, FaCheck } from "react-icons/fa";
|
||||||
import api from '../../config/api';
|
import api from '../../config/api';
|
||||||
// import { FaUser, FaSignOutAlt, FaBuilding, FaChevronDown, FaCheck } from 'react-icons/fa';
|
|
||||||
|
|
||||||
// import { RootState } from '../../store/store';
|
|
||||||
// import { UserState } from '../../store/slices/userSlice';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface AccountNavProps {
|
interface AccountNavProps {
|
||||||
accountName?: string | null;
|
accountName?: string | null;
|
||||||
transactionName?: string | null;
|
transactionName?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define the shape of state object
|
|
||||||
interface Account {
|
interface Account {
|
||||||
id: number;
|
id: number;
|
||||||
isDefault: boolean;
|
isDefault: boolean;
|
||||||
|
|
@ -23,32 +15,45 @@ interface Account {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UserData {
|
interface UserData {
|
||||||
id: string;
|
id: number;
|
||||||
firstName: string;
|
name: string;
|
||||||
lastName: string;
|
|
||||||
email: string;
|
email: string;
|
||||||
language: string;
|
language: string;
|
||||||
account: Account;
|
account: Account;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AccountDataArray {
|
interface AccountDataArray {
|
||||||
accountId: number, accountName: string, role: string, status: boolean
|
userId: number,
|
||||||
|
userEmail: string,
|
||||||
|
name: string,
|
||||||
|
accountId: number;
|
||||||
|
accountName: string;
|
||||||
|
role: string;
|
||||||
|
status: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to dispatch custom storage event
|
||||||
|
const dispatchSessionStorageChange = (key: string, newValue: any) => {
|
||||||
|
window.dispatchEvent(new CustomEvent('sessionStorageChange', {
|
||||||
|
detail: { key, newValue }
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
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 activeAccountId = JSON.parse(sessionStorage.getItem('activeAccountId') || 'null');
|
||||||
|
const userDetails = JSON.parse(sessionStorage.getItem('userDetails') || '{}');
|
||||||
|
console.log("activeAccountId", activeAccountId);
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [accountsOpen, setAccountsOpen] = useState(false);
|
const [accountsOpen, setAccountsOpen] = useState(false);
|
||||||
const [loadingAccounts, setLoadingAccounts] = useState(false);
|
const [loadingAccounts, setLoadingAccounts] = useState(false);
|
||||||
const [accounts, setAccounts] = useState<AccountDataArray[]>([]);
|
const [accounts, setAccounts] = useState<AccountDataArray[]>([]);
|
||||||
const [hasModalBeenClosed, setHasModalBeenClosed] = useState(false); // Track if modal was closed
|
const [hasModalBeenClosed, setHasModalBeenClosed] = useState(false);
|
||||||
const dropdownRef = useRef(null);
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const [user, setUser] = useState<UserData>({
|
const [user, setUser] = useState<UserData>({
|
||||||
id: "",
|
id: 0,
|
||||||
firstName: "",
|
name: "",
|
||||||
lastName: "",
|
|
||||||
email: "",
|
email: "",
|
||||||
language: "",
|
language: "",
|
||||||
account: {
|
account: {
|
||||||
|
|
@ -58,13 +63,17 @@ const AccountNav: React.FC<AccountNavProps> = ({ accountName = null, transaction
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log("user", user)
|
||||||
const getAccountDetails = async () => {
|
const getAccountDetails = async () => {
|
||||||
try {
|
try {
|
||||||
let res = await api.get('/api/user/accounts')
|
let res = await api.get('/api/user/accounts');
|
||||||
let items = res?.data
|
let items = res?.data;
|
||||||
console.log('items', items)
|
console.log('items', items);
|
||||||
// Extract only the fields you want
|
|
||||||
const cleanedData: AccountDataArray[] = items.map((item: any) => ({
|
const cleanedData: AccountDataArray[] = items.map((item: any) => ({
|
||||||
|
userId: item.userId,
|
||||||
|
userEmail: item.userEmail,
|
||||||
|
name: item.name,
|
||||||
accountId: item.accountId,
|
accountId: item.accountId,
|
||||||
accountName: item.accountName,
|
accountName: item.accountName,
|
||||||
role: item.role,
|
role: item.role,
|
||||||
|
|
@ -73,10 +82,9 @@ const AccountNav: React.FC<AccountNavProps> = ({ accountName = null, transaction
|
||||||
|
|
||||||
setAccounts(cleanedData);
|
setAccounts(cleanedData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleAccountsClick = async () => {
|
const handleAccountsClick = async () => {
|
||||||
setAccountsOpen(!accountsOpen);
|
setAccountsOpen(!accountsOpen);
|
||||||
|
|
@ -84,78 +92,127 @@ const AccountNav: React.FC<AccountNavProps> = ({ accountName = null, transaction
|
||||||
if (!accountsOpen) {
|
if (!accountsOpen) {
|
||||||
setLoadingAccounts(true);
|
setLoadingAccounts(true);
|
||||||
|
|
||||||
// Fetch API if:
|
|
||||||
// 1. No accounts data exists (first time), OR
|
|
||||||
// 2. Modal was previously closed (need fresh data)
|
|
||||||
if (accounts.length === 0 || hasModalBeenClosed) {
|
if (accounts.length === 0 || hasModalBeenClosed) {
|
||||||
await getAccountDetails();
|
await getAccountDetails();
|
||||||
setHasModalBeenClosed(false); // Reset the flag after fetching
|
setHasModalBeenClosed(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoadingAccounts(false);
|
setLoadingAccounts(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAccountSwitch = (account) => {
|
const handleAccountSwitch = (account: any) => {
|
||||||
setUser(prev => ({
|
console.log("handleAccountSwitch", account);
|
||||||
...prev,
|
|
||||||
account: { name: account.name, id: account.id }
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Update accounts active status
|
// Update sessionStorage
|
||||||
setAccounts(prevAccounts =>
|
sessionStorage.setItem('activeAccountId', JSON.stringify(account.accountId));
|
||||||
prevAccounts.map(acc => ({
|
|
||||||
...acc,
|
// Dispatch custom event
|
||||||
isActive: acc.id === account.id
|
dispatchSessionStorageChange('activeAccountId', account.accountId);
|
||||||
}))
|
|
||||||
);
|
// Update user state
|
||||||
|
|
||||||
|
setUser({
|
||||||
|
id: account.userId,
|
||||||
|
name: account.name,
|
||||||
|
email: account.userEmail,
|
||||||
|
language: "", // set it if available
|
||||||
|
account: {
|
||||||
|
id: account.accountId,
|
||||||
|
isDefault: false, // update as needed
|
||||||
|
name: account.accountName,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
sessionStorage.setItem('userDetails', JSON.stringify({
|
||||||
|
id: account.userId,
|
||||||
|
name: account.name,
|
||||||
|
email: account.userEmail,
|
||||||
|
language: "", // set it if available
|
||||||
|
account: {
|
||||||
|
id: account.accountId,
|
||||||
|
isDefault: false, // update as needed
|
||||||
|
name: account.accountName,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
setAccountsOpen(false);
|
setAccountsOpen(false);
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigate = useNavigate()
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const getUserDetails = async () => {
|
const getUserDetails = async () => {
|
||||||
try {
|
try {
|
||||||
let res = await api.get(`/api/user/details`)
|
let res = await api.get(`/api/user/details`);
|
||||||
console.log("getuser", res)
|
console.log("getuser", res);
|
||||||
setUser(res?.data)
|
const item = res?.data;
|
||||||
|
if (item) {
|
||||||
|
const userData = {
|
||||||
|
id: item?.id,
|
||||||
|
name : item?.firstName + " " + item?.lastName ,
|
||||||
|
email: item?.email,
|
||||||
|
language: "",
|
||||||
|
account: {
|
||||||
|
id: item?.account.id,
|
||||||
|
isDefault: false,
|
||||||
|
name: item?.account.name,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("userData",userData)
|
||||||
|
|
||||||
|
setUser(userData);
|
||||||
|
sessionStorage.setItem('userDetails', JSON.stringify(userData));
|
||||||
|
sessionStorage.setItem('activeAccountId', JSON.stringify(userData.account.id));
|
||||||
|
|
||||||
|
// Dispatch event for initial load
|
||||||
|
dispatchSessionStorageChange('activeAccountId', userData.account.id);
|
||||||
|
|
||||||
|
console.log("Data is saved in sessionStorage");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching user details:", error);
|
console.error("Error fetching user details:", error);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getUserDetails()
|
if (!activeAccountId) {
|
||||||
}, [])
|
getUserDetails();
|
||||||
|
} else {
|
||||||
|
setUser(userDetails);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
const pathnames = location.pathname
|
const rawPathnames = location.pathname
|
||||||
.split("/")
|
.split("/")
|
||||||
.filter((x) => x && isNaN(Number(x))); // remove numeric IDs
|
.filter((x) => x && isNaN(Number(x)));
|
||||||
|
|
||||||
|
// Special case: if first segment is 's' and next is 'settings', remove 's'
|
||||||
|
const pathnames =
|
||||||
|
rawPathnames[0] === "s" && rawPathnames[1] === "settings"
|
||||||
|
? rawPathnames.slice(1)
|
||||||
|
: rawPathnames;
|
||||||
|
|
||||||
const labelMap: Record<string, string> = {
|
const labelMap: Record<string, string> = {
|
||||||
accounts: "Accounts",
|
accounts: "Accounts",
|
||||||
transactions: "Transactions",
|
transactions: "Transactions",
|
||||||
templates: "Templates"
|
templates: "Templates"
|
||||||
// Add more routes here if needed
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// const [open, setOpen] = useState(false);
|
|
||||||
// const dropdownRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
// Close dropdown when clicked outside
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function handleClickOutside(event: MouseEvent) {
|
function handleClickOutside(event: MouseEvent) {
|
||||||
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
|
||||||
if (open) {
|
if (open) {
|
||||||
setHasModalBeenClosed(true); // Mark modal as closed when clicking outside
|
setHasModalBeenClosed(true);
|
||||||
}
|
}
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
setAccountsOpen(false); // Also close accounts dropdown
|
setAccountsOpen(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,24 +222,24 @@ const AccountNav: React.FC<AccountNavProps> = ({ accountName = null, transaction
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
try {
|
try {
|
||||||
let res = await api.post('/api/auth/logout')
|
let res = await api.post('/api/auth/logout');
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
|
sessionStorage.clear();
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
navigate("/login")
|
navigate("/login");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle main dropdown toggle
|
|
||||||
const handleMainDropdownToggle = () => {
|
const handleMainDropdownToggle = () => {
|
||||||
if (open) {
|
if (open) {
|
||||||
setHasModalBeenClosed(true); // Mark as closed when manually closing
|
setHasModalBeenClosed(true);
|
||||||
}
|
}
|
||||||
setOpen(!open);
|
setOpen(!open);
|
||||||
if (!open) {
|
if (!open) {
|
||||||
setAccountsOpen(false); // Close accounts dropdown when opening main dropdown
|
setAccountsOpen(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -191,7 +248,6 @@ const AccountNav: React.FC<AccountNavProps> = ({ accountName = null, transaction
|
||||||
.replace(/[-_]/g, " ")
|
.replace(/[-_]/g, " ")
|
||||||
.replace(/\b\w/g, (char) => char.toUpperCase());
|
.replace(/\b\w/g, (char) => char.toUpperCase());
|
||||||
|
|
||||||
|
|
||||||
const ShimmerRow = () => (
|
const ShimmerRow = () => (
|
||||||
<div className="animate-pulse px-4 py-3 border-b border-gray-100 last:border-b-0">
|
<div className="animate-pulse px-4 py-3 border-b border-gray-100 last:border-b-0">
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
|
|
@ -205,7 +261,6 @@ const AccountNav: React.FC<AccountNavProps> = ({ accountName = null, transaction
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
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 flex items-center flex-wrap">
|
<nav className="text-md font-medium text-gray-800 capitalize flex items-center flex-wrap">
|
||||||
|
|
@ -237,10 +292,7 @@ const AccountNav: React.FC<AccountNavProps> = ({ accountName = null, transaction
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
|
|
||||||
{/* User Dropdown */}
|
|
||||||
<div className="relative" ref={dropdownRef}>
|
<div className="relative" ref={dropdownRef}>
|
||||||
{/* Avatar Button */}
|
|
||||||
<button
|
<button
|
||||||
className="flex items-center space-x-2 p-1 cursor-pointer rounded-lg transition-colors"
|
className="flex items-center space-x-2 p-1 cursor-pointer rounded-lg transition-colors"
|
||||||
onClick={handleMainDropdownToggle}
|
onClick={handleMainDropdownToggle}
|
||||||
|
|
@ -249,34 +301,31 @@ const AccountNav: React.FC<AccountNavProps> = ({ accountName = null, transaction
|
||||||
>
|
>
|
||||||
<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">
|
<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 ? (
|
{user ? (
|
||||||
<span
|
<span className="w-full h-full object-cover p-1.5 font-bold">
|
||||||
className="w-full h-full object-cover p-1.5 font-bold"
|
{(user?.name?.slice(0, 2)).toUpperCase()}
|
||||||
>{(user?.firstName + " " + user?.lastName).split(' ').map((item) => item.slice(0, 1)).join("").toUpperCase()}</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<FaUser className="text-lg" />
|
<FaUser className="text-lg" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Enhanced Dropdown */}
|
|
||||||
{open && (
|
{open && (
|
||||||
<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">
|
<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">
|
||||||
{/* User Info Section */}
|
|
||||||
<div className="px-4 py-4 bg-gradient-to-r from-blue-50 to-purple-50 border-b border-gray-100">
|
<div className="px-4 py-4 bg-gradient-to-r from-blue-50 to-purple-50 border-b border-gray-100">
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<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 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">
|
||||||
{(user) ? (
|
{user ? (
|
||||||
<span
|
<span className="w-full h-full object-cover px-3 text-xl py-2.5 font-bold">
|
||||||
className="w-full h-full object-cover px-3 text-xl py-2.5 font-bold"
|
{(user?.name?.slice(0, 2)).toUpperCase()}
|
||||||
>
|
</span>
|
||||||
{(user?.firstName + " " + user?.lastName).split(' ').map((item) => item.slice(0, 1)).join("").toUpperCase()}</span>
|
|
||||||
) : (
|
) : (
|
||||||
<FaUser className="text-lg" />
|
<FaUser className="text-lg" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="font-semibold text-gray-900 truncate">{user.firstName + ' ' + user.lastName || '-'}</div>
|
<div className="font-semibold text-gray-900 truncate">{user?.name || '-'}</div>
|
||||||
<div className="text-sm text-gray-600 truncate">{user.email || '-'}</div>
|
<div className="text-sm text-gray-600 truncate">{user?.email || '-'}</div>
|
||||||
<div className="text-xs text-gray-500 mt-1">
|
<div className="text-xs text-gray-500 mt-1">
|
||||||
Account #{user?.account?.name || '-'}
|
Account #{user?.account?.name || '-'}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -284,7 +333,6 @@ const AccountNav: React.FC<AccountNavProps> = ({ accountName = null, transaction
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Account List Section */}
|
|
||||||
<div className="border-b border-gray-100">
|
<div className="border-b border-gray-100">
|
||||||
<button
|
<button
|
||||||
onClick={handleAccountsClick}
|
onClick={handleAccountsClick}
|
||||||
|
|
@ -297,7 +345,6 @@ const AccountNav: React.FC<AccountNavProps> = ({ accountName = null, transaction
|
||||||
<FaChevronDown className={`text-gray-400 transition-transform ${accountsOpen ? 'rotate-180' : ''}`} />
|
<FaChevronDown className={`text-gray-400 transition-transform ${accountsOpen ? 'rotate-180' : ''}`} />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Accounts Dropdown */}
|
|
||||||
{accountsOpen && (
|
{accountsOpen && (
|
||||||
<div className="bg-gray-50 max-h-64 overflow-y-auto">
|
<div className="bg-gray-50 max-h-64 overflow-y-auto">
|
||||||
{loadingAccounts ? (
|
{loadingAccounts ? (
|
||||||
|
|
@ -307,25 +354,25 @@ const AccountNav: React.FC<AccountNavProps> = ({ accountName = null, transaction
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="divide-y divide-gray-200">
|
<div className="divide-y divide-gray-200">
|
||||||
{accounts.length > 0 ? (accounts?.map((account) => (
|
{accounts?.length > 0 ? (
|
||||||
|
accounts?.map((account) => (
|
||||||
<button
|
<button
|
||||||
key={account.accountId}
|
key={account.accountId}
|
||||||
// onClick={() => handleAccountSwitch(account)}
|
onClick={() => handleAccountSwitch(account)}
|
||||||
className="w-full px-4 py-3 hover:bg-white transition-colors cursor-pointer text-left"
|
className={`w-full px-4 py-3 hover:bg-white transition-colors cursor-pointer text-left`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className={`flex items-center justify-between`}>
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<div className="w-8 h-8 rounded-full bg-gradient-to-br from-gray-200 to-gray-400 flex items-center justify-center text-white font-semibold text-xs">
|
<div className="w-8 h-8 rounded-full bg-gradient-to-br from-gray-200 to-gray-400 flex items-center justify-center text-white font-semibold text-xs">
|
||||||
{account.accountName.slice(0, 2).toUpperCase()}
|
{/* {account.accountName.slice(0, 2).toUpperCase()} */}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<span className="font-medium text-gray-900 text-sm">{account.accountName}</span>
|
<span className="font-medium text-gray-900 text-sm">{account.accountName}</span>
|
||||||
{account.status && (
|
{account.accountId === activeAccountId && (
|
||||||
<FaCheck className="text-green-500 text-xs" />
|
<FaCheck className="text-green-600 text-xs" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-600 truncate">{account.company}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-500 bg-gray-200 px-2 py-1 rounded">
|
<div className="text-xs text-gray-500 bg-gray-200 px-2 py-1 rounded">
|
||||||
|
|
@ -333,15 +380,16 @@ const AccountNav: React.FC<AccountNavProps> = ({ accountName = null, transaction
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
))) : <span className='text-gray-400 font-medium text-[12px] flex justify-center p-3'>No Account...</span>}
|
))
|
||||||
|
) : (
|
||||||
|
<span className='text-gray-400 font-medium text-[12px] flex justify-center p-3'>No Account...</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Logout Section */}
|
|
||||||
<div className="border-t border-gray-100">
|
<div className="border-t border-gray-100">
|
||||||
<button
|
<button
|
||||||
onClick={handleLogout}
|
onClick={handleLogout}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ const SideBar: React.FC<SideBarProps> = ({ showSideBar, setShowSideBar }) => {
|
||||||
headers: { "ngrok-skip-browser-warning": "69420" }
|
headers: { "ngrok-skip-browser-warning": "69420" }
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
sessionStorage.clear();
|
||||||
// Redirect to login or refresh the page after logout
|
// Redirect to login or refresh the page after logout
|
||||||
window.location.href = '/login';
|
window.location.href = '/login';
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
||||||
|
|
@ -61,8 +61,8 @@ const Home = () => {
|
||||||
trend: 'up'
|
trend: 'up'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Waiting for Others',
|
title: 'Draft',
|
||||||
count: data.waitingForOthers || 0,
|
count: data.draft || 0,
|
||||||
icon: <FaClock size={18} className="text-stone-600" />,
|
icon: <FaClock size={18} className="text-stone-600" />,
|
||||||
gradient: 'bg-gradient-to-br from-blue-50 to-blue-100',
|
gradient: 'bg-gradient-to-br from-blue-50 to-blue-100',
|
||||||
shadowColor: 'shadow-blue-200/50',
|
shadowColor: 'shadow-blue-200/50',
|
||||||
|
|
@ -70,69 +70,27 @@ const Home = () => {
|
||||||
textColor: 'text-blue-700',
|
textColor: 'text-blue-700',
|
||||||
trend: 'down'
|
trend: 'down'
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// title: 'declined',
|
|
||||||
// count: data.declined || 0,
|
|
||||||
// icon: <FaClock size={18} className="text-purple-600" />,
|
|
||||||
// gradient: 'bg-gradient-to-br from-purple-50 to-purple-100',
|
|
||||||
// shadowColor: 'shadow-purple-200/50',
|
|
||||||
// iconBg: 'bg-purple-100 border-purple-200',
|
|
||||||
// textColor: 'text-purple-700',
|
|
||||||
// trend: 'down'
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// title: 'cancelled',
|
|
||||||
// count: data.cancelled || 0,
|
|
||||||
// icon: <FaClock size={18} className="text-green-600" />,
|
|
||||||
// gradient: 'bg-gradient-to-br from-green-50 to-green-100',
|
|
||||||
// shadowColor: 'shadow-green-200/50',
|
|
||||||
// iconBg: 'bg-green-100 border-green-200',
|
|
||||||
// textColor: 'text-green-700',
|
|
||||||
// trend: 'down'
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// title: 'draft',
|
|
||||||
// count: data.draft || 0,
|
|
||||||
// icon: <FaClock size={18} className="text-stone-600" />,
|
|
||||||
// gradient: 'bg-gradient-to-br from-stone-50 to-stone-100',
|
|
||||||
// shadowColor: 'shadow-stone-200/50',
|
|
||||||
// iconBg: 'bg-stone-100 border-stone-200',
|
|
||||||
// textColor: 'text-stone-700',
|
|
||||||
// trend: 'down'
|
|
||||||
// }
|
|
||||||
// Add more cards here if needed
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [activeAccountId, setActiveAccountId] = useState<string | null>(
|
||||||
|
JSON.parse(sessionStorage.getItem('activeAccountId') || 'null')
|
||||||
|
);
|
||||||
const [dashboardData, setDashboardData] = useState<any>(null);
|
const [dashboardData, setDashboardData] = useState<any>(null);
|
||||||
const [fromDate, setFromDate] = useState<string>(''); // ISO string
|
const [fromDate, setFromDate] = useState<string>('');
|
||||||
const [toDate, setToDate] = useState<string>(''); // ISO string
|
const [toDate, setToDate] = useState<string>('');
|
||||||
const [cards, setCards] = useState<CardData[]>([]);
|
const [cards, setCards] = useState<CardData[]>([]);
|
||||||
const [accountID, setAccountID] = useState<number>()
|
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
console.log("cards", cards)
|
console.log("activeAccountId", activeAccountId);
|
||||||
|
console.log("cards", cards);
|
||||||
|
|
||||||
const getUserDetails = async () => {
|
const getDashboardData = async () => {
|
||||||
try {
|
if (!activeAccountId) return;
|
||||||
let res = await api.get('/api/user/details')
|
|
||||||
let accountIDData = res?.data?.account?.id
|
|
||||||
setAccountID(accountIDData)
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
setIsLoading(true);
|
||||||
getUserDetails()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const getDashboardData = async (from: string, to: string) => {
|
|
||||||
setIsLoading(true)
|
|
||||||
const payload = {
|
const payload = {
|
||||||
fromDate: from, // already ISO string
|
account: activeAccountId
|
||||||
toDate: to,
|
|
||||||
account : accountID
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -144,7 +102,7 @@ const Home = () => {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching dashboard data:", error);
|
console.error("Error fetching dashboard data:", error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false)
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -155,16 +113,41 @@ const Home = () => {
|
||||||
setToDate(isoTo);
|
setToDate(isoTo);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Listen for sessionStorage changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (fromDate && toDate && accountID) {
|
const handleStorageChange = (e: StorageEvent) => {
|
||||||
getDashboardData(fromDate, toDate);
|
if (e.key === 'activeAccountId') {
|
||||||
|
const newActiveAccountId = e.newValue ? JSON.parse(e.newValue) : null;
|
||||||
|
setActiveAccountId(newActiveAccountId);
|
||||||
}
|
}
|
||||||
}, [fromDate, toDate , accountID]);
|
};
|
||||||
|
|
||||||
|
// Listen for storage events (works for changes from other tabs)
|
||||||
|
window.addEventListener('storage', handleStorageChange);
|
||||||
|
|
||||||
|
// Custom event listener for same-tab changes
|
||||||
|
const handleCustomStorageChange = (e: CustomEvent) => {
|
||||||
|
if (e.detail.key === 'activeAccountId') {
|
||||||
|
setActiveAccountId(e.detail.newValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('sessionStorageChange', handleCustomStorageChange as EventListener);
|
||||||
|
|
||||||
// Set default range on mount (last 7 days)
|
return () => {
|
||||||
|
window.removeEventListener('storage', handleStorageChange);
|
||||||
|
window.removeEventListener('sessionStorageChange', handleCustomStorageChange as EventListener);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Re-fetch dashboard data when activeAccountId changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeAccountId) {
|
||||||
|
getDashboardData();
|
||||||
|
}
|
||||||
|
}, [activeAccountId]);
|
||||||
|
|
||||||
|
// Set default date range on mount (last 7 days)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const from = new Date();
|
const from = new Date();
|
||||||
|
|
|
||||||
|
|
@ -277,10 +277,10 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
|
||||||
tagsData = groupByRecipientId(tagsData);
|
tagsData = groupByRecipientId(tagsData);
|
||||||
sendTagsToRecipients(tagsData, accountId, transactionId);
|
sendTagsToRecipients(tagsData, accountId, transactionId);
|
||||||
|
|
||||||
// (async () => {
|
(async () => {
|
||||||
// const res = await api.put(`/api/accounts/${accountId}/transactions/${transactionId}`, {status: "SENT"})
|
const res = await api.put(`/api/accounts/${accountId}/transactions/${transactionId}`, {status: "SENT"})
|
||||||
// console.log(res, "Check123")
|
console.log(res, "Check123")
|
||||||
// })()
|
})()
|
||||||
}, [isSend])
|
}, [isSend])
|
||||||
// Save Tags - END
|
// Save Tags - END
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,9 @@ interface AccountOption {
|
||||||
|
|
||||||
const Setting = () => {
|
const Setting = () => {
|
||||||
const { page1 } = useParams<{ page1: string }>();
|
const { page1 } = useParams<{ page1: string }>();
|
||||||
|
console.log("page1" , page1)
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
console.log("location",location)
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
// Refs for subsections within each main tab
|
// Refs for subsections within each main tab
|
||||||
|
|
@ -337,8 +339,8 @@ const Setting = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SettingNav />
|
{/* <SettingNav /> */}
|
||||||
{/* <AccountNav/> */}
|
<AccountNav/>
|
||||||
<div className="bg-[#f9fafa] h-[calc(90vh-20px)] flex flex-col mt-2">
|
<div className="bg-[#f9fafa] h-[calc(90vh-20px)] flex flex-col mt-2">
|
||||||
{/* Main Navigation Tabs */}
|
{/* Main Navigation Tabs */}
|
||||||
<div className="sticky top-0 z-10 bg-white border-b border-gray-200 px-8 pt-5">
|
<div className="sticky top-0 z-10 bg-white border-b border-gray-200 px-8 pt-5">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue