diff --git a/src/components/CommonNav.tsx b/src/components/CommonNav.tsx index 03f4792..794f18f 100644 --- a/src/components/CommonNav.tsx +++ b/src/components/CommonNav.tsx @@ -1,12 +1,14 @@ import React, { useEffect, useRef, useState } from 'react'; import { Link, useLocation, useNavigate } from 'react-router-dom'; -import { FaSignOutAlt, FaUser, FaBuilding, FaChevronDown, FaCheck } from "react-icons/fa"; +import { FaSignOutAlt, FaUser, FaBuilding, FaChevronDown, FaCheck, FaBars } from "react-icons/fa"; import api from '../config/api'; - interface AccountNavProps { accountName?: string | null; transactionName?: string | null; + showSideBar?: boolean; + setShowSideBar?: (show: boolean) => void; + isMobile?: boolean; } interface Account { @@ -40,7 +42,11 @@ const dispatchSessionStorageChange = (key: string, newValue: any) => { })); }; -const CommonNav: React.FC = () => { +const CommonNav: React.FC = ({ + showSideBar, + setShowSideBar, + isMobile = false +}) => { const activeAccountId = JSON.parse(sessionStorage.getItem('activeAccountId') || 'null'); const userDetails = JSON.parse(sessionStorage.getItem('userDetails') || '{}'); console.log("activeAccountId", activeAccountId); @@ -112,7 +118,6 @@ const CommonNav: React.FC = () => { dispatchSessionStorageChange('activeAccountId', account.accountId); // Update user state - setUser({ id: account.userId, name: account.name, @@ -141,7 +146,6 @@ const CommonNav: React.FC = () => { setOpen(false); }; - const navigate = useNavigate(); const getUserDetails = async () => { @@ -173,8 +177,6 @@ const CommonNav: React.FC = () => { console.log("Data is saved in sessionStorage"); } - - } catch (error) { console.error("Error fetching user details:", error); } @@ -194,17 +196,7 @@ const CommonNav: React.FC = () => { .split("/") .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 = { - accounts: "Accounts", - transactions: "Transactions", - templates: "Templates" - }; useEffect(() => { function handleClickOutside(event: MouseEvent) { @@ -244,11 +236,39 @@ const CommonNav: React.FC = () => { } }; + const handleSidebarToggle = () => { + if (setShowSideBar) { + setShowSideBar(!showSideBar); + } + }; + + // 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 = { + accounts: "Accounts", + transactions: "Transactions", + templates: "Templates", + }; + const formatSegment = (segment: string) => segment .replace(/[-_]/g, " ") .replace(/\b\w/g, (char) => char.toUpperCase()); + // If URL has accounts → transactions, only keep "transactions" + let filteredPathnames = [...pathnames]; + const accIndex = pathnames.indexOf("accounts"); + const txnIndex = pathnames.indexOf("transactions"); + + if (accIndex !== -1 && txnIndex !== -1 && txnIndex > accIndex) { + // keep only "transactions" and everything after it + filteredPathnames = pathnames.slice(txnIndex); + } + const ShimmerRow = () => (
@@ -263,34 +283,51 @@ const CommonNav: React.FC = () => { ); return ( -
- + + {/* Breadcrumb Navigation */} + +
@@ -407,6 +444,20 @@ const CommonNav: React.FC = () => {
); -}; + // const ShimmerRow = () => ( + //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ // ); + // }; + +} export default CommonNav; \ No newline at end of file diff --git a/src/components/accounts/Account.tsx b/src/components/accounts/Account.tsx index 31fee35..5fca92c 100644 --- a/src/components/accounts/Account.tsx +++ b/src/components/accounts/Account.tsx @@ -249,7 +249,7 @@ const Account = () => {
- +
{isModalOpen && setIsModalOpen(false)} onInvite={handleInvite} inviteLoading={inviteLoading} diff --git a/src/components/accounts/TransactionDataTable.tsx b/src/components/accounts/TransactionDataTable.tsx index 0488efc..61471d5 100644 --- a/src/components/accounts/TransactionDataTable.tsx +++ b/src/components/accounts/TransactionDataTable.tsx @@ -22,16 +22,13 @@ const statusColorMap = { }; -const TransactionDataTable = () => { +const TransactionDataTable = ({activeAccountId}) => { const navigate = useNavigate() const loaction = useLocation() console.log("loaction", loaction.pathname); const [currentEmailId, setCurrentEmailId] = useState('null'); const [selectedTransactions, setSelectedTransactions] = useState([]); - const [activeAccountId, setActiveAccountId] = useState( - JSON.parse(sessionStorage.getItem('activeAccountId') || 'null') - ); const [loading, setLoading] = useState(false) const fetchTransactions = async () => { diff --git a/src/components/auth/LoginForm.tsx b/src/components/auth/LoginForm.tsx index 0d21871..8ad99b8 100644 --- a/src/components/auth/LoginForm.tsx +++ b/src/components/auth/LoginForm.tsx @@ -108,6 +108,7 @@ function LoginForm() { .then((res) => { console.log(res.status, "Response Status Line 42 LoginForm.tsx", res); if (res.status === 200) { + toast.dismiss(); // console.log('User Logined Successfully Line 45 LoginForm.tsx headerCookie:', res.headers.authorization); // Cookies.set("authorization", res.headers.authorization, { diff --git a/src/components/contacts/ContactsFormModal.tsx b/src/components/contacts/ContactsFormModal.tsx index c349e0c..0e44ffa 100644 --- a/src/components/contacts/ContactsFormModal.tsx +++ b/src/components/contacts/ContactsFormModal.tsx @@ -170,113 +170,71 @@ const ContactFormModal = ({ return ( -
-
-
-

{`${actionStatus} Contact`}

+
+
+
+

{`${actionStatus} Contact`}

-
- {[ - { label: "First Name", field: "firstName" }, - { label: "Last Name", field: "lastName" }, - { label: "Email", field: "email", fieldType: "email" }, - { label: "Title", field: "title" }, - { label: "Company", field: "company" }, - { label: "Website", field: "website", fieldType: "url" }, - { label: "Phone", field: "phone", fieldType: "tel" }, - ].map(({ label, field, fieldType }) => ( -
- - - handleChange(field as keyof ContactData, e.target.value) - } - required={true} - className="px-3 py-1.5 border border-gray-400 rounded-md focus:outline-none focus:ring-1 focus:ring-[var(--primary)] text-sm" - placeholder={`Enter ${label}`} - /> - {errors[field] && ( - - {errors[field]} - - )} -
- ))} +
+ {[ + { label: "First Name", field: "firstName" }, + { label: "Last Name", field: "lastName" }, + { label: "Email", field: "email", fieldType: "email" }, + { label: "Title", field: "title" }, + { label: "Company", field: "company" }, + { label: "Website", field: "website", fieldType: "url" }, + { label: "Phone", field: "phone", fieldType: "tel" }, + ].map(({ label, field, fieldType }) => ( +
+ + + handleChange(field as keyof ContactData, e.target.value) + } + required + className="px-3 py-1.5 border border-gray-400 rounded-md focus:outline-none focus:ring-1 focus:ring-[var(--primary)] text-sm" + placeholder={`Enter ${label}`} + /> + {errors[field] && ( + {errors[field]} + )}
+ ))} +
- {/*
- -
- {formData?.phones?.map((phone, index) => ( -
-
- handlePhoneChange(index, e.target.value)} - className="flex-1 px-3 py-1.5 border border-gray-400 rounded-md focus:outline-none focus:ring-1 focus:ring-[var(--primary)] text-sm" - maxLength={15} - placeholder='Enter Phone Numbers' - /> - {formData.phones.length > 1 && ( - - )} -
- {errors[`phones_${index}`] && ( - {errors[`phones_${index}`]} - )} -
- ))} - - -
-
*/} - -
- - -
-
+
+ +
+
+
+ ); }; diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index 9e821d7..4464eca 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -1,3 +1,4 @@ +// Dashboard.jsx - Updated version import React, { useEffect, useLayoutEffect } from 'react' import SideBar from './SideBar' import { Outlet, useNavigate } from 'react-router-dom' @@ -22,6 +23,7 @@ import CommonNav from '../CommonNav' function Dashboard() { const [showSideBar, setShowSideBar] = React.useState(true) const [showConfetti, setShowConfetti] = React.useState(false) + const [isMobile, setIsMobile] = React.useState(false) const navigate = useNavigate(); const dispatch = useDispatch(); @@ -74,8 +76,6 @@ function Dashboard() { } }, []) - - useEffect(() => { if (transactionStatus === TRANSACTION_STATUS_ENUMS.TRANSACTION_SEND_SUCCESS || transactionStatus === TRANSACTION_STATUS_ENUMS.TRANSACTION_INITIATED) return; @@ -102,35 +102,55 @@ function Dashboard() { dispatch(hideLoader()); }, [documentSignType]) + useEffect(() => { + if (showConfetti) { + const duration = 3000; + const end = Date.now() + duration; - useEffect(() => { - if (showConfetti) { - const duration = 3000; - const end = Date.now() + duration; + const interval = setInterval(() => { + if (Date.now() > end) { + clearInterval(interval); + return; + } - const interval = setInterval(() => { - if (Date.now() > end) { - clearInterval(interval); - return; - } + confetti({ + particleCount: 80, // more particles + angle: Math.random() * 360, // fire in random direction + spread: 220, // wider spread + startVelocity: 15, // more power + scalar: 1.2, // bigger pieces (default is 1) + origin: { + x: Math.random(), + y: Math.random() * 0.6, + }, + zIndex: 9999, + }); + }, 350); // fire every 150ms + } + }, [showConfetti]); - confetti({ - particleCount: 80, // more particles - angle: Math.random() * 360, // fire in random direction - spread: 220, // wider spread - startVelocity: 15, // more power - scalar: 1.2, // bigger pieces (default is 1) - origin: { - x: Math.random(), - y: Math.random() * 0.6, - }, - zIndex: 9999, - }); - }, 350); // fire every 150ms + useEffect(() => { + // Function to handle window resize + const handleResize = () => { + const mobile = window.innerWidth <= 768; + setIsMobile(mobile); + + if (mobile) { + setShowSideBar(false); // hide sidebar on mobile by default + } else { + setShowSideBar(true); // show sidebar on desktop } - }, [showConfetti]); + }; + // Set sidebar visibility on mount + handleResize(); + // Add event listener + window.addEventListener('resize', handleResize); + + // Cleanup listener on unmount + return () => window.removeEventListener('resize', handleResize); + }, []); return ( <> @@ -165,19 +185,44 @@ function Dashboard() { { user?.authorized &&
- {/* Complet DashBoard Back Side SideBar */} + {/* Mobile Overlay when sidebar is open */} + {isMobile && showSideBar && ( +
setShowSideBar(false)} + /> + )} + + {/* Complete Dashboard Back Side SideBar */}
- + {/* On Top Main Content - START */} -
+
{/* Main Content */} -
- +
+
+ {/* On Top Main Content - END */}
diff --git a/src/components/dashboard/SendForSign.tsx b/src/components/dashboard/SendForSign.tsx index 5ed29db..386ada4 100644 --- a/src/components/dashboard/SendForSign.tsx +++ b/src/components/dashboard/SendForSign.tsx @@ -77,52 +77,97 @@ const SendForSign: React.FC = ({ showSideBar }) => { // } return ( -
setSendForSignDropdown(false)} > -
-
handleStartSectionClick(SignTypeEnum.multipleSign)} - className={`w-full`}> +
handleStartSectionClick(SignTypeEnum.multipleSign)} + className={`w-full`}> { showSideBar ? -

Send for signature

+

Send for signature

: }
+ {showSideBar &&

+ className=' border-[var(--sendForSignTextCol)] pe-2 py-2'> setSendForSignDropdown(!sendForSignDropdown)} className={`text-2xl ${sendForSignDropdown ? 'rotate-180' : ''} transition-all duration-150`} />

} +
*/} + + +
+
handleStartSectionClick(SignTypeEnum.multipleSign)} + className={` + flex items-center justify-center + bg-[var(--sidebarDropDownBg)] + text-center + px-2 py-2.5 + overflow-hidden + transition-all duration-300 ease-in-out w-full + ${showSideBar ? "rounded-l" : "rounded"} + `} + style={{ whiteSpace: "nowrap" }} + > + {showSideBar ? ( +

Send for signature

+ ) : ( + + )} +
+ + {showSideBar && ( +
+ setSendForSignDropdown(!sendForSignDropdown)} + className={`text-2xl transition-transform duration-150 ${sendForSignDropdown ? "rotate-180" : "" + }`} + /> +
+ )}
+ {/* Drop Down Box */} { sendForSignDropdown && -
-
handleStartSectionClick(SignTypeEnum.selfSign)}> -

Self Sign

-

You're the only signer.

+
+
handleStartSectionClick(SignTypeEnum.selfSign)} + > +

Self Sign

+

You're the only signer.

-
handleStartSectionClick(SignTypeEnum.multipleSign)}> -

Send for Sign

-

You and other need to sign.

+
handleStartSectionClick(SignTypeEnum.multipleSign)} + > +

Send for Sign

+

You and other need to sign.

} +
) } diff --git a/src/components/dashboard/SideBar.tsx b/src/components/dashboard/SideBar.tsx index b6463a6..a6ff16f 100644 --- a/src/components/dashboard/SideBar.tsx +++ b/src/components/dashboard/SideBar.tsx @@ -1,3 +1,4 @@ +// SideBar.jsx - Updated version import React, { useState } from 'react'; import { Link, useLocation } from 'react-router-dom'; import NavLink from './NavLink'; @@ -16,6 +17,7 @@ import logoSME from '../../assets/SMELogo.png' type SideBarProps = { showSideBar: boolean; setShowSideBar: React.Dispatch>; + isMobile?: boolean; }; type MenuPathMap = { @@ -30,7 +32,7 @@ const menuPathMap: MenuPathMap = { '/contacts': 'Contacts' }; -const SideBar: React.FC = ({ showSideBar, setShowSideBar }) => { +const SideBar: React.FC = ({ showSideBar, setShowSideBar, isMobile = false }) => { const location = useLocation(); const [showLogoutModal, setShowLogoutModal] = useState(false); @@ -54,7 +56,17 @@ const SideBar: React.FC = ({ showSideBar, setShowSideBar }) => { return ( <> {/* Side Bar */} -
+
{/* Menu */}

@@ -76,10 +88,26 @@ const SideBar: React.FC = ({ showSideBar, setShowSideBar }) => { {/* SideBar Toggle Button */}
setShowSideBar(!showSideBar)} - className='absolute top-[100%] translate-y-[-100%] right-0 bg-[var(--dashBoardBg)] p-2 rounded-tl-sm rounded-bl-sm cursor-pointer transition-colors' + className={` + absolute top-[95%] translate-y-[-60%] + ${isMobile + ? (showSideBar ? 'right-2' : 'right-2') + : 'right-0 top-[100%] translate-y-[-100%]' + } + bg-[var(--dashBoardBg)] p-1 + ${isMobile + ? 'rounded-md' + : (showSideBar ? 'rounded-tl-sm rounded-bl-sm' : 'rounded-tl-sm rounded-bl-sm') + } + cursor-pointer transition-colors + `} > -
-
+ {!isMobile && ( + <> +
+
+ + )} {showSideBar ? ( @@ -113,7 +141,7 @@ const SideBar: React.FC = ({ showSideBar, setShowSideBar }) => { {/* Logout Confirmation Modal */} {showLogoutModal && (
-
+

Confirm Logout

Are you sure you want to log out?

diff --git a/src/components/home/StartSection.tsx b/src/components/home/StartSection.tsx index 1807ef3..f1d07ec 100644 --- a/src/components/home/StartSection.tsx +++ b/src/components/home/StartSection.tsx @@ -47,6 +47,9 @@ const cards = [ const StartSection = () => { const navigate = useNavigate(); const dispatch = useDispatch() + const [activeAccountId, setActiveAccountId] = useState( + JSON.parse(sessionStorage.getItem('activeAccountId') || 'null') + ); const user = useSelector((state: RootState) => state.user); if (!user.account || user.account === null) { @@ -81,6 +84,35 @@ const StartSection = () => { // } // }, []) + + + // Listen for sessionStorage changes + useEffect(() => { + 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); + }; + }, []); + return (

@@ -95,7 +127,7 @@ const StartSection = () => { {/* Recent Data Section */}
- +
diff --git a/src/components/process/CompletionScreen/DocumentCompletionScreen.tsx b/src/components/process/CompletionScreen/DocumentCompletionScreen.tsx index ff8a318..496b4e4 100644 --- a/src/components/process/CompletionScreen/DocumentCompletionScreen.tsx +++ b/src/components/process/CompletionScreen/DocumentCompletionScreen.tsx @@ -47,8 +47,8 @@ type Signer = { const SignersList: React.FC<{ signers: Signer[] }> = ({ signers }) => { return ( //
-
-
+
+

Signature Status @@ -58,7 +58,7 @@ const SignersList: React.FC<{ signers: Signer[] }> = ({ signers }) => {

-
+
{signers.map((signer) => (
diff --git a/src/components/process/processStepOne/SelfSignWarnModal.tsx b/src/components/process/processStepOne/SelfSignWarnModal.tsx index fc83e0c..f0dc991 100644 --- a/src/components/process/processStepOne/SelfSignWarnModal.tsx +++ b/src/components/process/processStepOne/SelfSignWarnModal.tsx @@ -9,7 +9,7 @@ const SelfSignWarnModal = ({ setShowSelfSignWarnModal }: PropsType) => { const navigate = useNavigate() return ( -
+

Confirm Self Sign

diff --git a/src/components/setting/Setting.tsx b/src/components/setting/Setting.tsx index d53aded..806c5d2 100644 --- a/src/components/setting/Setting.tsx +++ b/src/components/setting/Setting.tsx @@ -9,7 +9,7 @@ import AsideSection from "./AsideSection"; import Signature from "./Signature/Signature"; import { Bounce, toast } from "react-toastify"; import AccountNav from "../accounts/AccountNav"; -import ContactForm , {ContactData} from "./ContactForm"; +import ContactForm, { ContactData } from "./ContactForm"; // Define the shape of state object interface Account { @@ -392,7 +392,7 @@ const Setting = () => { const handleContactSubmit = async (data: ContactData) => { try { setCreateAndInviteLoader(true); - const res = await api.post(`/api/accounts/${activeAccountId}/contacts`, [data]); + const res = await api.post(`/api/accounts/${activeAccountId}/contacts`, [data]); if (res.status === 200) { toast.success('Contact created successfully!', { position: "top-center", @@ -415,16 +415,16 @@ const Setting = () => { {/* */} {/* */}

- {/* Main Navigation Tabs */} -
-
+ {/* Tabs */} +
+
{tabs.map((tab) => tab.type === page1 && (
- {/* Content with Aside Navigation */} -
- {/* Fixed Aside Navigation - Shows subsections of active tab */} - {activeTab === 'userProfile' && -
+ {/* Responsive Layout */} +
+ + {/* Sidebar (hidden on small, shown on lg) */} + {activeTab === 'userProfile' && ( +
{ />
- } - {/* Scrollable Main Content */} + )} + + {/* Sidebar as horizontal nav (only on mobile) */} + {activeTab === 'userProfile' && ( +
+
+ {getSubSections(activeTab).map((sub) => ( + + ))} +
+
+ )} + + {/* Main Content */}
{
+ ) } diff --git a/src/components/setting/Signature/AddSignatureModal.tsx b/src/components/setting/Signature/AddSignatureModal.tsx index 25473ad..54e7e30 100644 --- a/src/components/setting/Signature/AddSignatureModal.tsx +++ b/src/components/setting/Signature/AddSignatureModal.tsx @@ -55,6 +55,7 @@ const AddSignatureModal: React.FC = ({ getAllSignatures, editingSignature }) => { + console.log("editingSignature", editingSignature) const [activeTab, setActiveTab] = useState<'SELECT' | 'DRAW' | 'UPLOAD'>('SELECT'); const [fullName, setFullName] = useState(''); const [initials, setInitials] = useState(''); @@ -81,6 +82,8 @@ const AddSignatureModal: React.FC = ({ signatureOptions.length > 0 ? { id: signatureOptions[0].id, style: signatureOptions[0].style } : { id: 0, style: "" } ); + console.log("selectedSignature", selectedSignature) + // Store the actual file const [file1, setFile1] = useState(null); const [file2, setFile2] = useState(null); @@ -107,10 +110,9 @@ const AddSignatureModal: React.FC = ({ // Navigation items const navigationItems = [ - { id: 'SELECT', label: 'SELECT', icon: }, - { id: 'DRAW', label: 'DRAW', icon: }, - { id: 'UPLOAD', label: 'UPLOAD', icon: }, - // { id: 'SELECT', label: 'SELECT', icon: }, + { id: 'SELECT', label: 'SELECT', icon: }, + { id: 'DRAW', label: 'DRAW', icon: }, + { id: 'UPLOAD', label: 'UPLOAD', icon: }, ]; // Initialize form with editing signature data @@ -193,25 +195,43 @@ const AddSignatureModal: React.FC = ({ const handleSubmit = async () => { setIsSubmitting(true); - if (activeTab === 'DRAW' || activeTab === 'UPLOAD') { - if (!signatures.fullSignature || !signatures.initials) { - alert('Please provide both signature and initials'); - setIsSubmitting(false); - return; - } + // Only validate for DRAW and UPLOAD + if ((activeTab === 'DRAW' || activeTab === 'UPLOAD') && + (!signatures.fullSignature || !signatures.initials)) { + // alert('Please provide both signature and initials'); + toast.error('Please provide both signature and initials in draw and upload case', { + position: "top-center", + autoClose: 2000, + hideProgressBar: false, + closeOnClick: false, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "light", + transition: Bounce, + }); + setIsSubmitting(false); + return; } try { + // Base payload const payload = { signatureType: activeTab, - signatureBase64: signatures.fullSignature, - initialsBase64: signatures.initials, firstName: fullName.split(' ')[0] || user.firstName, lastName: fullName.split(' ')[1] || user.lastName, font: selectedSignature.style, default: checked }; + // Only add signatures if not SELECT + if (activeTab !== 'SELECT') { + payload.signatureBase64 = signatures.fullSignature; + payload.initialsBase64 = signatures.initials; + } + + console.log("payload214", payload); + let res; if (isEditMode && editingSignature) { // Update existing signature @@ -273,32 +293,57 @@ const AddSignatureModal: React.FC = ({ } }; + + // Fixed renderContent function for SELECT case with proper radio button styling const renderContent = () => { switch (activeTab) { case 'SELECT': return ( -
-
-

Choose Signature Style

-

Select from pre-designed signature styles

+
+
+

Choose Signature Style

+

Select from pre-designed signature styles

-
-
+
+
{signatureOptions.map((option) => ( -
+
setSelectedSignature({ id: option.id, style: option.style })} + > + {/* Custom Radio Button */} +
+
+ {selectedSignature.id === option.id && ( +
+ )} +
+
+ + {/* Hidden native radio button for accessibility */} setSelectedSignature({ id: option.id, style: option.style })} - className="w-4 h-4 text-blue-600 focus:ring-blue-500" + className="sr-only" /> -
-
-
Signed by:
-
+
+
Signed by:
+
= ({ }`}> {fullName || option.name}
-
+
dca3e646-62cd-4cc...
-
-
DS
-
+
DS
+
= ({ case 'DRAW': return ( - +
+ +
); case 'UPLOAD': return (
-
-

Upload Signature

+
+

Upload Signature

+

Upload your signature images

-
-
-
- {/* Upload 1 */} -
- - - handleImageChange(e, setFile1, setPreview1, "fullSignature")} - /> -
+
+
+ {/* Upload 1 - Full Signature */} +
+ + + handleImageChange(e, setFile1, setPreview1, "fullSignature")} + /> +
- {/* Upload 2 */} -
- - - handleImageChange(e, setFile2, setPreview2, "initials")} - /> -
+ {/* Upload 2 - Initials */} +
+ + + handleImageChange(e, setFile2, setPreview2, "initials")} + />
); - // case 'SELECT': - // return ( - //
- //
- //

Select Signature

- //
- //
- // ); default: return null; } @@ -430,25 +469,147 @@ const AddSignatureModal: React.FC = ({ getUserDetails(); }, []); - return ( -
-
- {/* Sidebar Navigation */} -
- {/* Header */} -
-
-
-

Add Signature

-

Create your digital signature

+ + + return ( +
+
+ + {/* Mobile Navigation - Fixed at top on mobile only */} +
+ + {/* Scrollable area (mobile: inputs + nav + content) */} +
+
+

+ {isEditMode ? 'Edit Signature' : 'Add Signature'} +

+
+ {/* Mobile Inputs */} +
+
+
+ + setFullName(e.target.value)} + placeholder="Enter full name" + className="w-full px-2 py-2 text-xs border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500" + /> +
+
+ + setInitials(e.target.value)} + placeholder="Enter initials" + className="w-full px-2 py-2 text-xs border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500" + /> +
+
+
+ + {/* Mobile Navigation - Sticky inside scroll */} +
+
+ +
+
+ + {/* Main Content */} +
+
+ {renderContent()}
- {/* Form Inputs */} -
-
+ {/* Checkbox - Fixed */} +
+ +
+ + {/* Footer Buttons - Fixed */} +
+ + +
+
+ + + {/* Desktop Sidebar Navigation */} +
+ {/* Header - Fixed */} +
+

+ {isEditMode ? 'Edit Signature' : 'Add Signature'} +

+

+ {isEditMode ? 'Update your digital signature' : 'Create your digital signature'} +

+
+ + {/* Form Inputs - Fixed */} +
+
-
- {/* Navigation Menu */} -
-
{/* Main Content Area */} -
- {/* Header with close button */} - - {/* Content */} -
- {/* */} - {renderContent()} +
+ {/* Content - Scrollable */} +
+
+ {renderContent()} +
-
-
); + }; export default AddSignatureModal; \ No newline at end of file diff --git a/src/components/setting/Signature/DrawSignatureCanvas.tsx b/src/components/setting/Signature/DrawSignatureCanvas.tsx index 250218e..7129951 100644 --- a/src/components/setting/Signature/DrawSignatureCanvas.tsx +++ b/src/components/setting/Signature/DrawSignatureCanvas.tsx @@ -224,20 +224,20 @@ const DrawSignatureCanvas: React.FC = ({ onSignatureChange, initialSignat return (
-
-

Draw Signature

-

Create your signature by drawing directly on the canvas

+
+

Draw Signature

+

Create your signature by drawing directly on the canvas

-
-
+
+
{/* Full Signature Canvas */} -
- +
+
= ({ onSignatureChange, initialSignat
{/* Initials Canvas */} -
+
= ({ onSignatureChange, initialSignat
{/* Controls */} -
+
:
-
+
<> {/* Header */}
@@ -126,107 +163,121 @@ const SignatureManager: React.FC = () => {
{/* Table Card */} -
- {/* Table Header */} -
-
-
-

Signature

-
-
-

Initial

-
-
-

Actions

+ +
+
+ {/* Table Header */} +
+
+
+

Signature

+
+
+

Initial

+
+
+

Actions

+
-
- {/* Table Body */} -
- {signatures.map((signature, index) => ( -
- {/* Signature Column */} -
-
- {/* Label */} -
- Signed by: -
- - {/* Signature Image */} -
- Signature -
- - {/* Signature ID */} -
- {signature.signatureHash} -
-
-
- - {/* Initial Column */} -
-
- {/* Label */} -
- Initials -
- - {/* Signature Image */} -
- DS Signature -
- {/* Initial ID */} -
- {signature.initialsHash} -
-
-
- - {/* Actions Column */} -
-
- - - {/* Dropdown Menu */} - {activeDropdown === signature.id && ( -
- - + {/* Table Body */} +
+ {signatures.map((signature, index) => ( +
+ {/* Signature Column */} +
+
+ {/* Label */} +
+ Signed by:
- )} + + {/* Signature Image or Name/Initials */} +
+ {signature?.signatureType !== 'SELECT' ? ( + Signature + ) : ( + renderSignatureText(signature?.signatureType, signature?.font, signature?.firstName, signature?.lastName, 'name') + )} +
+ + {/* Signature ID */} +
+ {signature.signatureHash} +
+
+ +
+ + {/* Initial Column */} +
+
+ {/* Label */} +
+ Initials +
+ + {/* Signature Initials: Image or Generated Text */} +
+ {signature?.signatureType !== 'SELECT' ? ( + Initials + ) : ( + renderSignatureText(signature?.signatureType, signature?.font, signature?.firstName, signature?.lastName, 'initials') + )} +
+ + {/* Initial ID */} +
+ {signature.initialsHash} +
+
+
+ + + {/* Actions Column */} +
+
+ + + {/* Dropdown Menu */} + {activeDropdown === signature.id && ( +
+ + +
+ )} +
-
- ))} + ))} +
@@ -252,8 +303,8 @@ const SignatureManager: React.FC = () => { )} {showAddSignature && ( - diff --git a/src/components/setting/UserProfile.tsx b/src/components/setting/UserProfile.tsx index 6cb75a5..1b8e654 100644 --- a/src/components/setting/UserProfile.tsx +++ b/src/components/setting/UserProfile.tsx @@ -1,62 +1,57 @@ -import { FiX, FiEdit2 } from 'react-icons/fi'; +import { FiX, FiEdit2 } from "react-icons/fi"; + interface Account { - id: number; - isDefault: boolean; - name: string; + id: number; + isDefault: boolean; + name: string; } interface UserData { - id: number; - name:string - email: string; - language: string; - account: Account; + id: number; + name: string; + email: string; + language: string; + account: Account; } interface UserProfileProps { - userData: UserData; + userData: UserData; } + const UserProfile: React.FC = ({ userData }) => { - const getInitials = (firstName: string, lastName: string) => { - return `${firstName.charAt(0)}${lastName.charAt(0)}`.toUpperCase(); - }; - return ( -
- {/* Profile Picture */} -
-
- {userData.name.slice(0,2).toUpperCase()} -
-
+ return ( +
+ {/* Profile Picture */} +
+
+ {userData.name.slice(0, 2).toUpperCase()} +
+
- {/* Info Fields */} -
- {/* Row: Name */} -
-
Name
-
- {userData.name} -
-
- - {/* Row: Email */} -
-
Email
-
{userData.email}
-
- - {/* Row: Account Name */} -
-
Account name
-
- {userData.account.name} -
-
-
+ {/* Info Fields */} +
+ {/* Row: Name */} +
+
Name
+
{userData.name}
+ {/* Row: Email */} +
+
Email
+
{userData.email}
+
- ); + {/* Row: Account Name */} +
+
Account name
+
+ {userData.account.name} +
+
+
+
+ ); }; -export default UserProfile \ No newline at end of file +export default UserProfile; diff --git a/src/components/templates/TemplateTable.tsx b/src/components/templates/TemplateTable.tsx index 1c1b75d..6939105 100644 --- a/src/components/templates/TemplateTable.tsx +++ b/src/components/templates/TemplateTable.tsx @@ -41,6 +41,34 @@ const TemplateTable = () => { }, [activeAccountId]) + // Listen for sessionStorage changes + useEffect(() => { + 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 formatDate = (timestamp: number) => { return new Date(timestamp * 1000).toLocaleString(); }; diff --git a/src/components/templates/Templates.tsx b/src/components/templates/Templates.tsx index 8f0c866..4a13bab 100644 --- a/src/components/templates/Templates.tsx +++ b/src/components/templates/Templates.tsx @@ -28,6 +28,8 @@ const Templates = () => { const filters = ["All", "My Templates", "Shared"]; + + const handleAddTemplate = async () => { console.log("handleAddTemplate called"); try { @@ -39,23 +41,23 @@ const Templates = () => { } - - + + return (
{/* Header */} -
+
{/* Filter as pill buttons */} -
+
{filters.map((filter) => (
+ {/* Add Template Button */}
- -
+ {/* Table */}
- +
); }; diff --git a/src/components/transactions/AuditModal.tsx b/src/components/transactions/AuditModal.tsx index 2317b98..60f5d19 100644 --- a/src/components/transactions/AuditModal.tsx +++ b/src/components/transactions/AuditModal.tsx @@ -126,11 +126,12 @@ const AuditModal: React.FC = ({ closeModal }) => { ]; const getActionIcon = (action: string) => { + const icons = { 'CREATED': , 'SENT': , 'INVITED': , - 'COMPLETED': , + 'COMPLETED': , 'RE_SENT_INVITATION': , 'RE_SENT_EXPIRED_INVITATION': , 'VIEWED': , @@ -148,7 +149,7 @@ const AuditModal: React.FC = ({ closeModal }) => { 'REASSIGNED': , 'EDITED': , }; - + console.log("getActionIcons", action || ) return icons[action as keyof typeof icons] || ; }; @@ -218,15 +219,12 @@ const AuditModal: React.FC = ({ closeModal }) => { }; return ( -
-
+
+
+ {/* Header */}
-
-

- Audit Status -

-
+

Audit Status

+ ); }; diff --git a/src/components/transactions/TransactionView.tsx b/src/components/transactions/TransactionView.tsx index 01fdf25..d37e1bb 100644 --- a/src/components/transactions/TransactionView.tsx +++ b/src/components/transactions/TransactionView.tsx @@ -91,7 +91,7 @@ const TransactionView = () => { const fetchDocuments = async () => { try { - const res = await api.get(`https://concrete-gladly-mullet.ngrok-free.app/api/accounts/${accountId}/transactions/${transactionId}/documents?include_images=true`) + const res = await api.get(`/api/accounts/${accountId}/transactions/${transactionId}/documents`) if (res?.status === 200) { let resDoc = res?.data setDocumentsData(resDoc) @@ -112,15 +112,27 @@ const TransactionView = () => { window.open(`./../../../../sign/${id}/${SignTypeEnum.selfSign}`) } + const getStatusBadge = (status: string) => { + const statusClasses = { + 'SENT': 'bg-blue-100 text-blue-800', + 'COMPLETED': 'bg-green-100 text-green-800', + 'FAILED': 'bg-red-100 text-red-800', + 'PENDING': 'bg-yellow-100 text-yellow-800', + 'VIEWED': 'bg-purple-100 text-purple-800', + 'REJECTED': 'bg-red-100 text-red-800' + }; + return statusClasses[status as keyof typeof statusClasses] || 'bg-gray-100 text-gray-800'; + }; + return ( <> {transactionDetails && <> -
-
+
+
{/* Header */}

- Complete with Forms Made Easy: {transactionDetails?.transaction?.subject ?? "Untitled"} + Complete with Sign Made Easy: {transactionDetails?.transaction?.subject ?? "Untitled"}

@@ -170,38 +182,55 @@ const TransactionView = () => { {/* Recipients List */} {transactionDetails.recipients.map((recipient, index) => (
-
-
- {/*

{index+1}

*/} +
+ + {/* Left section: recipient info */} +
-

{recipient.name}

-

{recipient.email}

- {recipient.hostEmail !== null && recipient.hostEmail !== "" &&

Host: {recipient.hostEmail}

} +

{recipient.name}

+

{recipient.email}

+ {recipient.hostEmail && ( +

+ Host: {recipient.hostEmail} +

+ )}
-
-
-
- -

{recipient.status}

+ {/* Right section: status and actions */} +
+
+
+ +

{recipient.status}

- {recipient.email === user.email && recipient.status !== "COMPLETED" && -

handleNavigateToSign(recipient.id)} - className='text-white rounded-sm bg-[var(--primary)] px-3 py-1 font-semibold cursor-pointer'> + className="text-white rounded-sm bg-[var(--primary)] px-3 py-1 font-semibold text-xs hover:bg-opacity-90 transition" + > Sign -

} + + )}
- {recipient.status !== "PENDING" &&

on {recipient.completedAt}

} - {/* - View signed location - */} + + {recipient.status !== "PENDING" && ( +

on {recipient.completedAt}

+ )} + {/* Optional signed document link */} + {/* {recipient.viewLink && ( + + View signed location + + )} */}
-
+ +
))} +
} @@ -219,19 +248,25 @@ const TransactionView = () => {
}
-
+
{documentsData.map((docsD, index) => (

{docsD.name}

- {docsD?.pages[0]?.src ? - - : - } + {docsD?.thumbnail ? ( + + ) : ( + + )}
-

- 1 of {docsD?.pages?.length} - +

+ {/* 1 of {docsD?.pages?.length} */} +

diff --git a/src/index.css b/src/index.css index c174e93..6786b51 100644 --- a/src/index.css +++ b/src/index.css @@ -59,6 +59,18 @@ } +.btn { + cursor: pointer; + transition: transform 0.2s ease, box-shadow 0.2s ease; +} + +/* Button press effect */ +.btn:active { + transform: scale(0.97) translateY(1px); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) inset; +} + + /* Sortable CSS */ .sortable-ghost {