Template
This commit is contained in:
parent
b65614ae69
commit
772dec4858
|
|
@ -60,6 +60,12 @@ const App = () => {
|
||||||
</ProtectedRoute>
|
</ProtectedRoute>
|
||||||
} />
|
} />
|
||||||
|
|
||||||
|
<Route path="/process-step-two/:accountId/template/:templateId" element={
|
||||||
|
<ProtectedRoute>
|
||||||
|
<ProcessStepTwo />
|
||||||
|
</ProtectedRoute>
|
||||||
|
} />
|
||||||
|
|
||||||
<Route path="/process-step-two/:accountId/:transactionId" element={
|
<Route path="/process-step-two/:accountId/:transactionId" element={
|
||||||
<ProtectedRoute>
|
<ProtectedRoute>
|
||||||
<ProcessStepTwo />
|
<ProcessStepTwo />
|
||||||
|
|
|
||||||
|
|
@ -272,8 +272,6 @@ const Account = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Cards type="transactions" onOpenModal={handleOpenModal} cards={cards}
|
<Cards type="transactions" onOpenModal={handleOpenModal} cards={cards}
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ const SigningOrderDiagram: React.FC<{ signers: Signer[] }> = ({ signers }) => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Dotted line */}
|
{/* Dotted line */}
|
||||||
<div className="flex items-center">
|
<div className="flex items-center mb-3">
|
||||||
<div className="w-16 border-t-2 border-dotted border-gray-300"></div>
|
<div className="w-16 border-t-2 border-dotted border-gray-300"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -129,7 +129,7 @@ const SigningOrderDiagram: React.FC<{ signers: Signer[] }> = ({ signers }) => {
|
||||||
<div className="flex items-center justify-center space-x-6">
|
<div className="flex items-center justify-center space-x-6">
|
||||||
{sortedOrders.map((order, index) => (
|
{sortedOrders.map((order, index) => (
|
||||||
<React.Fragment key={order}>
|
<React.Fragment key={order}>
|
||||||
<div className="flex flex-col items-center space-y-4">
|
<div className="flex flex-col items-center space-y-4 mb-4">
|
||||||
<div className="text-sm font-medium text-gray-600 mb-2">
|
<div className="text-sm font-medium text-gray-600 mb-2">
|
||||||
Step {order}
|
Step {order}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,17 @@ interface DocumentCardProps {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
isCompleted: boolean;
|
isCompleted: boolean;
|
||||||
|
transactionTemplateToggle:boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DocumentCard: React.FC<DocumentCardProps> = ({ id, name, isCompleted }) => {
|
const DocumentCard: React.FC<DocumentCardProps> = ({ id, name, isCompleted , transactionTemplateToggle }) => {
|
||||||
|
const params = useParams<{ accountId?: string; transactionId?: string; templateId?: string }>();
|
||||||
const { accountId, transactionId } = useParams<{ accountId: string, transactionId: string }>();
|
const accountId = params.accountId;
|
||||||
if (!accountId || !transactionId) {
|
const transactionId = params.transactionId || params.templateId;
|
||||||
window.location.href = "/";
|
// if (!accountId || !transactionId) {
|
||||||
return null;
|
// window.location.href = "/";
|
||||||
}
|
// return null;
|
||||||
|
// }
|
||||||
|
|
||||||
const [progress, setProgress] = useState<number>(0)
|
const [progress, setProgress] = useState<number>(0)
|
||||||
const [message, setMessage] = useState<string>('')
|
const [message, setMessage] = useState<string>('')
|
||||||
|
|
@ -96,7 +98,7 @@ const DocumentCard: React.FC<DocumentCardProps> = ({ id, name, isCompleted }) =>
|
||||||
console.error("Missing document ID, account ID, or transaction ID");
|
console.error("Missing document ID, account ID, or transaction ID");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const res = await api.delete(`/api/accounts/${accountId}/transactions/${transactionId}/documents/${docId}`)
|
const res = await api.delete(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}/documents/${docId}`)
|
||||||
if (res.status === 202) {
|
if (res.status === 202) {
|
||||||
dispatch(deleteDocument(docId));
|
dispatch(deleteDocument(docId));
|
||||||
setDocumentDel({ docId: null, isDel: false });
|
setDocumentDel({ docId: null, isDel: false });
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ const ProcessStepOne = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS;
|
let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS;
|
||||||
|
console.log("transactionTemplateToggle1", transactionTemplateToggle)
|
||||||
|
console.log()
|
||||||
const templateId = useParams<{ templateId: string }>().templateId;
|
const templateId = useParams<{ templateId: string }>().templateId;
|
||||||
let { accountId, transactionId } = useParams<{ accountId: string, transactionId: string }>();
|
let { accountId, transactionId } = useParams<{ accountId: string, transactionId: string }>();
|
||||||
|
|
||||||
|
|
@ -31,14 +33,15 @@ const ProcessStepOne = () => {
|
||||||
|
|
||||||
if (!transactionId) {
|
if (!transactionId) {
|
||||||
transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES;
|
transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES;
|
||||||
|
console.log("transactionTemplateToggle2", transactionTemplateToggle)
|
||||||
transactionId = templateId;
|
transactionId = templateId;
|
||||||
dispatch(toggleToTemplates());
|
dispatch(toggleToTemplates());
|
||||||
}
|
}
|
||||||
console.log("ProcessStepOne called with accountId:", accountId, "and transactionId:", transactionId);
|
console.log("ProcessStepOne called with accountId:", accountId, "and transactionId:", transactionId);
|
||||||
if (!accountId || !transactionId) {
|
// if (!accountId || !transactionId) {
|
||||||
window.location.href = "/";
|
// window.location.href = "/";
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments)
|
// const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments)
|
||||||
// const { selectedDocuments, signType } = useSelector((state: RootState) => state.selectedDocuments)
|
// const { selectedDocuments, signType } = useSelector((state: RootState) => state.selectedDocuments)
|
||||||
|
|
@ -46,6 +49,8 @@ const ProcessStepOne = () => {
|
||||||
const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
|
const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
|
||||||
const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients)
|
const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients)
|
||||||
|
|
||||||
|
console.log("selectedRecipients", selectedRecipients)
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
selectedRecipients.length === 0 && dispatch(fetchRecipients({ accountId, transactionId }));
|
selectedRecipients.length === 0 && dispatch(fetchRecipients({ accountId, transactionId }));
|
||||||
|
|
@ -61,6 +66,8 @@ const ProcessStepOne = () => {
|
||||||
dispatch(hideLoader());
|
dispatch(hideLoader());
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
|
||||||
const handleNextClick = async (isSendNow: boolean) => {
|
const handleNextClick = async (isSendNow: boolean) => {
|
||||||
let formError = {
|
let formError = {
|
||||||
documents: false,
|
documents: false,
|
||||||
|
|
@ -138,28 +145,38 @@ const ProcessStepOne = () => {
|
||||||
accessCode: "",
|
accessCode: "",
|
||||||
accessCodeDelivery: true,
|
accessCodeDelivery: true,
|
||||||
language: "en",
|
language: "en",
|
||||||
name: `${recipient.firstName} ${recipient.lastName}`
|
name: `${recipient.firstName} ${recipient.lastName}`,
|
||||||
|
roleName: recipient.roleName
|
||||||
}));
|
}));
|
||||||
|
|
||||||
dispatch(addRecipientAsync({ recipientsData, accountId, transactionId }));
|
dispatch(addRecipientAsync({ recipientsData, accountId, transactionId }));
|
||||||
dispatch(showLoader("Processing Message..."));
|
dispatch(showLoader("Processing Message..."));
|
||||||
|
|
||||||
await api.put(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`, { subject: emailDtls.subject, message: emailDtls.message, status: "CREATED" }).then((res) => {
|
console.log(`1/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`)
|
||||||
console.log(res);
|
|
||||||
|
let res = await api.put(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`, {
|
||||||
|
subject: emailDtls.subject, message: emailDtls.message,
|
||||||
|
status: "CREATED"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
console.log("Line 158", res.data)
|
||||||
if (isSendNow) {
|
if (isSendNow) {
|
||||||
dispatch(updateTransactionStatus(TRANSACTION_STATUS_ENUMS.TRANSACTION_SEND_SUCCESS));
|
dispatch(updateTransactionStatus(TRANSACTION_STATUS_ENUMS.TRANSACTION_SEND_SUCCESS));
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// navigate("/");
|
// navigate("/");
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
dispatch(resetDocuments())
|
dispatch(resetDocuments())
|
||||||
dispatch(resetRecipients())
|
dispatch(resetRecipients())
|
||||||
dispatch(hideLoader());
|
dispatch(hideLoader());
|
||||||
navigate(`/process-step-two/${accountId}/${transactionId}`);
|
if (transactionTemplateToggle === 'templates') {
|
||||||
|
console.log(`/process-step-two/${accountId}/template/${transactionId}`)
|
||||||
|
navigate(`/process-step-two/${accountId}/template/${transactionId}`)
|
||||||
|
} else {
|
||||||
|
navigate(`/process-step-two/${accountId}/${transactionId}`);
|
||||||
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -223,7 +240,7 @@ const ProcessStepOne = () => {
|
||||||
{/* Documents */}
|
{/* Documents */}
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-lg font-medium mb-4 text-[var(--processStepOneTextHead)]">Documents</h2>
|
<h2 className="text-lg font-medium mb-4 text-[var(--processStepOneTextHead)]">Documents</h2>
|
||||||
<SelectedDocuments />
|
<SelectedDocuments transactionTemplateToggle={transactionTemplateToggle} />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Recipients */}
|
{/* Recipients */}
|
||||||
|
|
@ -232,7 +249,7 @@ const ProcessStepOne = () => {
|
||||||
<div className="flex flex-col gap-4 align-start">
|
<div className="flex flex-col gap-4 align-start">
|
||||||
<RecipientCards />
|
<RecipientCards />
|
||||||
{/* {signType !== "self-sign" && <RecipientAddCard />} */}
|
{/* {signType !== "self-sign" && <RecipientAddCard />} */}
|
||||||
<RecipientAddCard />
|
<RecipientAddCard transactionTemplateToggle={transactionTemplateToggle} />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import { AppDispatch } from "../../../store/store";
|
||||||
import api from "../../../config/api";
|
import api from "../../../config/api";
|
||||||
import ContactModal from "./ContactModal";
|
import ContactModal from "./ContactModal";
|
||||||
import useDebouncedEffect from "../../../utlity/useDebounce";
|
import useDebouncedEffect from "../../../utlity/useDebounce";
|
||||||
|
import { IoIosSettings } from "react-icons/io";
|
||||||
|
import { FiEdit, FiTrash } from "react-icons/fi";
|
||||||
|
|
||||||
interface ContactType {
|
interface ContactType {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -19,7 +21,7 @@ export interface ContactTypeObj {
|
||||||
contacts: ContactType[];
|
contacts: ContactType[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const RecipientAddCard = () => {
|
const RecipientAddCard = ({ transactionTemplateToggle }) => {
|
||||||
|
|
||||||
const [contactData, setContactData] = useState<ContactTypeObj>({ type: "", contacts: [] });
|
const [contactData, setContactData] = useState<ContactTypeObj>({ type: "", contacts: [] });
|
||||||
const [openContactModal, setOpenContactModal] = useState(false);
|
const [openContactModal, setOpenContactModal] = useState(false);
|
||||||
|
|
@ -27,7 +29,10 @@ const RecipientAddCard = () => {
|
||||||
const dispatch = useDispatch<AppDispatch>();
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
const formRef = useRef<HTMLFormElement | null>(null);
|
const formRef = useRef<HTMLFormElement | null>(null);
|
||||||
|
|
||||||
const [recipient, setRecipients] = useState<Recipient>({ firstName: "", lastName: "", email: "", hostName: "", hostEmail: "", type: recipientType.SIGNER });
|
const [recipient, setRecipients] = useState<Recipient>({ firstName: "", lastName: "", email: "", hostName: "", hostEmail: "", roleName: "", type: recipientType.SIGNER, allowSenderToEdit: Boolean, allowSenderToRemove: Boolean });
|
||||||
|
|
||||||
|
const [showAdvanceSettings, setShowAdvanceSettings] = useState(false)
|
||||||
|
console.log(recipient)
|
||||||
|
|
||||||
const handleRecipientAdd = (e: React.FormEvent<HTMLFormElement>) => {
|
const handleRecipientAdd = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -43,24 +48,24 @@ const RecipientAddCard = () => {
|
||||||
// Validation Ends
|
// Validation Ends
|
||||||
|
|
||||||
dispatch(addRecipient(recipient));
|
dispatch(addRecipient(recipient));
|
||||||
setRecipients({ firstName: "", lastName: "", email: "", hostName: "", hostEmail: "", type: recipientType.SIGNER });
|
setRecipients({ firstName: "", lastName: "", email: "", hostName: "", hostEmail: "", roleName: "", type: recipientType.SIGNER, allowSenderToEdit: Boolean, allowSenderToRemove: Boolean });
|
||||||
formRef.current?.reset();
|
formRef.current?.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// if (!recipient.firstName.length) return;
|
// if (!recipient.firstName.length) return;
|
||||||
|
|
||||||
// const handler = setTimeout(() => {
|
// const handler = setTimeout(() => {
|
||||||
// api.get(`/api/contact?firstName=${recipient.firstName}`)
|
// api.get(`/api/contact?firstName=${recipient.firstName}`)
|
||||||
// .then(res => {
|
// .then(res => {
|
||||||
// setContactData({ type: "firstName", contacts: res.data.content });
|
// setContactData({ type: "firstName", contacts: res.data.content });
|
||||||
// })
|
// })
|
||||||
// .catch(err => console.log(err));
|
// .catch(err => console.log(err));
|
||||||
// }, 500); // wait 500ms after the last change
|
// }, 500); // wait 500ms after the last change
|
||||||
|
|
||||||
// return () => {
|
// return () => {
|
||||||
// clearTimeout(handler); // cancel previous timeout if effect runs again
|
// clearTimeout(handler); // cancel previous timeout if effect runs again
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// }, [recipient.firstName]);
|
// }, [recipient.firstName]);
|
||||||
|
|
||||||
|
|
@ -81,7 +86,7 @@ const RecipientAddCard = () => {
|
||||||
// })
|
// })
|
||||||
// .catch(err => console.log(err))
|
// .catch(err => console.log(err))
|
||||||
// }, [recipient.lastName], 500);
|
// }, [recipient.lastName], 500);
|
||||||
|
|
||||||
// useDebouncedEffect(() => {
|
// useDebouncedEffect(() => {
|
||||||
// if (!recipient.email.length) return
|
// if (!recipient.email.length) return
|
||||||
// api.get(`/api/contact?email=${recipient.email}`)
|
// api.get(`/api/contact?email=${recipient.email}`)
|
||||||
|
|
@ -99,114 +104,238 @@ const RecipientAddCard = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
ref={formRef}
|
ref={formRef}
|
||||||
onSubmit={handleRecipientAdd}
|
onSubmit={handleRecipientAdd}
|
||||||
className="w-full bg-white border border-gray-200 rounded-lg shadow-md px-6 py-6 space-y-5 relative"
|
className="w-full bg-white border border-gray-200 rounded-lg shadow-md px-6 py-4 space-y-3 relative"
|
||||||
>
|
|
||||||
{/* Name Inputs */}
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
||||||
<input
|
|
||||||
// required
|
|
||||||
type="text"
|
|
||||||
onFocus={() => setOpenContactModal(true)}
|
|
||||||
onBlur={handleContactModalBlur}
|
|
||||||
value={recipient.firstName}
|
|
||||||
placeholder="First Name"
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition"
|
|
||||||
onChange={(e) => setRecipients({ ...recipient, firstName: e.target.value })}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
required
|
|
||||||
type="text"
|
|
||||||
onFocus={() => setOpenContactModal(true)}
|
|
||||||
onBlur={handleContactModalBlur}
|
|
||||||
value={recipient.lastName}
|
|
||||||
placeholder="Last Name"
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition"
|
|
||||||
onChange={(e) => setRecipients({ ...recipient, lastName: e.target.value })}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{recipient.type === recipientType.INPERSONSIGNER && <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
||||||
<input
|
|
||||||
required
|
|
||||||
type="text"
|
|
||||||
onFocus={() => setOpenContactModal(true)}
|
|
||||||
onBlur={handleContactModalBlur}
|
|
||||||
value={recipient.hostName}
|
|
||||||
placeholder="Host Name"
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition"
|
|
||||||
onChange={(e) => setRecipients({ ...recipient, hostName: e.target.value })}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
required
|
|
||||||
type="email"
|
|
||||||
onFocus={() => setOpenContactModal(true)}
|
|
||||||
onBlur={handleContactModalBlur}
|
|
||||||
value={recipient.hostEmail}
|
|
||||||
placeholder="Host Email"
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition"
|
|
||||||
onChange={(e) => setRecipients({ ...recipient, hostEmail: e.target.value })}
|
|
||||||
/>
|
|
||||||
</div>}
|
|
||||||
|
|
||||||
{/* Email Input */}
|
|
||||||
<input
|
|
||||||
required
|
|
||||||
type="email"
|
|
||||||
onFocus={() => setOpenContactModal(true)}
|
|
||||||
onBlur={handleContactModalBlur}
|
|
||||||
value={recipient.email}
|
|
||||||
placeholder="Recipient Email"
|
|
||||||
className="w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition"
|
|
||||||
onChange={(e) => setRecipients({ ...recipient, email: e.target.value })}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Contact Modal */}
|
|
||||||
{contactData.contacts.length > 0 && openContactModal && (
|
|
||||||
<div className="absolute z-50 mt-1 w-full max-w-2xl bg-white shadow-lg rounded-md p-3 border border-gray-100">
|
|
||||||
<ContactModal
|
|
||||||
contactData={contactData}
|
|
||||||
inpFirstName={recipient.firstName}
|
|
||||||
inpLastName={recipient.lastName}
|
|
||||||
inpEmail={recipient.email}
|
|
||||||
openContactModal={openContactModal}
|
|
||||||
setRecipients={setRecipients}
|
|
||||||
setOpenContactModal={setOpenContactModal}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Signing options + Add button in one line */}
|
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
|
||||||
<div className="flex flex-wrap gap-6">
|
|
||||||
{[
|
|
||||||
{ label: "Signer", value: recipientType.SIGNER },
|
|
||||||
{ label: "In Person Signer", value: recipientType.INPERSONSIGNER },
|
|
||||||
{ label: "CC", value: recipientType.CC },
|
|
||||||
].map((option) => (
|
|
||||||
<label key={option.value} className="flex items-center gap-2 text-gray-700 text-sm cursor-pointer">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="recipient-type"
|
|
||||||
value={option.value}
|
|
||||||
defaultChecked={option.value === recipientType.SIGNER}
|
|
||||||
onClick={(e) => setRecipients({ ...recipient, type: e.target.value })}
|
|
||||||
className="accent-[var(--primary)] cursor-pointer"
|
|
||||||
/>
|
|
||||||
{option.label}
|
|
||||||
</label>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="inline-flex items-center gap-2 bg-[var(--primary)] hover:bg-[var(--primaryLight2)] text-white text-sm font-semibold px-5 py-2 rounded-md transition"
|
|
||||||
>
|
>
|
||||||
<FaPlus size={14} /> Add Recipient
|
{transactionTemplateToggle === 'templates' && (
|
||||||
</button>
|
<div className="space-y-1">
|
||||||
</div>
|
{/* Top Row: Role Input + Settings */}
|
||||||
</form>
|
<div className="flex justify-between items-center">
|
||||||
|
{/* Role Name Input */}
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
onFocus={() => setOpenContactModal(true)}
|
||||||
|
onBlur={handleContactModalBlur}
|
||||||
|
value={recipient.roleName}
|
||||||
|
placeholder="Role name"
|
||||||
|
className="w-64 px-3 py-2 me-3 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition"
|
||||||
|
onChange={(e) => setRecipients({ ...recipient, roleName: e.target.value })}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Settings + Permissions */}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="flex justify-between">
|
||||||
|
{/* Permission Status Text - Always Visible when settings open */}
|
||||||
|
<div
|
||||||
|
className={`transition-all duration-600 ease-in-out ${showAdvanceSettings ? 'max-h-10 opacity-100 me-3' : 'max-h-0 opacity-0 '
|
||||||
|
} overflow-hidden`}
|
||||||
|
>
|
||||||
|
<div className="text-sm text-gray-600">
|
||||||
|
{recipient.allowSenderToEdit && recipient.allowSenderToRemove && "Full permissions granted"}
|
||||||
|
{recipient.allowSenderToEdit && !recipient.allowSenderToRemove && "Edit permission granted"}
|
||||||
|
{!recipient.allowSenderToEdit && recipient.allowSenderToRemove && "Delete permission granted"}
|
||||||
|
{!recipient.allowSenderToEdit && !recipient.allowSenderToRemove && "No permissions granted"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* Animated Permission Controls */}
|
||||||
|
<div className={`overflow-visible transition-all duration-900 ease-in-out ${showAdvanceSettings ? 'max-w-80 opacity-100' : 'max-w-0 opacity-0'
|
||||||
|
}`}>
|
||||||
|
<div className="flex items-center space-x-3 pr-1.5">
|
||||||
|
{/* Edit Permission */}
|
||||||
|
<div className="relative group">
|
||||||
|
<label
|
||||||
|
className={`relative flex items-center justify-center w-6 h-6 border-2 rounded-lg cursor-pointer transition-all duration-300 transform hover:scale-105 ${recipient.allowSenderToEdit
|
||||||
|
? 'bg-blue-100 border-blue-500 shadow-lg'
|
||||||
|
: 'bg-white border-gray-300 hover:border-blue-400'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={recipient.allowSenderToEdit}
|
||||||
|
onChange={(e) =>
|
||||||
|
setRecipients({ ...recipient, allowSenderToEdit: e.target.checked })
|
||||||
|
}
|
||||||
|
onFocus={() => setOpenContactModal(true)}
|
||||||
|
onBlur={handleContactModalBlur}
|
||||||
|
className="absolute w-full h-full opacity-0 cursor-pointer"
|
||||||
|
/>
|
||||||
|
<FiEdit
|
||||||
|
className={`w-3 h-3 pointer-events-none transition-all duration-300 ${recipient.allowSenderToEdit ? 'text-blue-600' : 'text-gray-400'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
{/* Tooltip */}
|
||||||
|
<div className="absolute bottom-0 -translate-y-full left-1/2 transform -translate-x-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none z-20">
|
||||||
|
<div className="bg-gray-800 text-white text-xs rounded-lg py-2 px-3 whitespace-nowrap">
|
||||||
|
Allow Edit Permission
|
||||||
|
<div className="absolute bottom-[-5px] left-1/2 transform -translate-x-1/2 border-4 border-transparent border-b-gray-800"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Delete Permission */}
|
||||||
|
<div className="relative group">
|
||||||
|
<label
|
||||||
|
className={`relative flex items-center justify-center w-6 h-6 border-2 rounded-lg cursor-pointer transition-all duration-300 transform hover:scale-105 ${recipient.allowSenderToRemove
|
||||||
|
? 'bg-red-100 border-red-500 shadow-lg'
|
||||||
|
: 'bg-white border-gray-300 hover:border-red-400'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={recipient.allowSenderToRemove}
|
||||||
|
onChange={(e) =>
|
||||||
|
setRecipients({ ...recipient, allowSenderToRemove: e.target.checked })
|
||||||
|
}
|
||||||
|
onFocus={() => setOpenContactModal(true)}
|
||||||
|
onBlur={handleContactModalBlur}
|
||||||
|
className="absolute w-full h-full opacity-0 cursor-pointer"
|
||||||
|
/>
|
||||||
|
<FiTrash
|
||||||
|
className={`w-3 h-3 pointer-events-none transition-all duration-300 ${recipient.allowSenderToRemove ? 'text-red-600' : 'text-gray-400'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
{/* Tooltip */}
|
||||||
|
<div className="absolute bottom-0 -translate-y-full left-1/2 transform -translate-x-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none z-20">
|
||||||
|
<div className="bg-gray-800 text-white text-xs rounded-lg py-2 px-3 whitespace-nowrap">
|
||||||
|
Allow Delete Permission
|
||||||
|
<div className="absolute bottom-[-5px] left-1/2 transform -translate-x-1/2 border-4 border-transparent border-b-gray-800"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Settings Icon */}
|
||||||
|
<div className="relative group">
|
||||||
|
<p
|
||||||
|
className="text-blue-600 hover:text-blue-700 cursor-pointer transition-colors duration-200 p-2 rounded-full hover:bg-blue-50"
|
||||||
|
onClick={() => setShowAdvanceSettings((prev) => !prev)}
|
||||||
|
>
|
||||||
|
<IoIosSettings
|
||||||
|
className={`h-6 w-6 transition-transform duration-300 ${showAdvanceSettings ? 'rotate-90' : 'rotate-0'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
{/* Tooltip */}
|
||||||
|
<div className="absolute top-0 -translate-y-full left-1/2 transform -translate-x-1/2 mb-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none z-20">
|
||||||
|
<div className="bg-gray-800 text-white text-xs rounded-lg py-2 px-3 whitespace-nowrap">
|
||||||
|
Advanced Settings
|
||||||
|
<div className="absolute bottom-[-6px] left-1/2 transform -translate-x-1/2 border-4 border-transparent border-b-gray-800"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Name Inputs */}
|
||||||
|
<div className="grid grid-cols-12 gap-4 mt-3">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
onFocus={() => setOpenContactModal(true)}
|
||||||
|
onBlur={handleContactModalBlur}
|
||||||
|
value={recipient.firstName}
|
||||||
|
placeholder="First Name"
|
||||||
|
className="col-span-4 w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition"
|
||||||
|
onChange={(e) => setRecipients({ ...recipient, firstName: e.target.value })}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
onFocus={() => setOpenContactModal(true)}
|
||||||
|
onBlur={handleContactModalBlur}
|
||||||
|
value={recipient.lastName}
|
||||||
|
placeholder="Last Name"
|
||||||
|
className="col-span-4 w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition"
|
||||||
|
onChange={(e) => setRecipients({ ...recipient, lastName: e.target.value })}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
type="email"
|
||||||
|
onFocus={() => setOpenContactModal(true)}
|
||||||
|
onBlur={handleContactModalBlur}
|
||||||
|
value={recipient.email}
|
||||||
|
placeholder="Recipient Email"
|
||||||
|
className="col-span-4 w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition"
|
||||||
|
onChange={(e) => setRecipients({ ...recipient, email: e.target.value })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{recipient.type === recipientType.INPERSONSIGNER && <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
onFocus={() => setOpenContactModal(true)}
|
||||||
|
onBlur={handleContactModalBlur}
|
||||||
|
value={recipient.hostName}
|
||||||
|
placeholder="Host Name"
|
||||||
|
className="w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition"
|
||||||
|
onChange={(e) => setRecipients({ ...recipient, hostName: e.target.value })}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
type="email"
|
||||||
|
onFocus={() => setOpenContactModal(true)}
|
||||||
|
onBlur={handleContactModalBlur}
|
||||||
|
value={recipient.hostEmail}
|
||||||
|
placeholder="Host Email"
|
||||||
|
className="w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition"
|
||||||
|
onChange={(e) => setRecipients({ ...recipient, hostEmail: e.target.value })}
|
||||||
|
/>
|
||||||
|
</div>}
|
||||||
|
|
||||||
|
{/* Contact Modal */}
|
||||||
|
{contactData.contacts.length > 0 && openContactModal && (
|
||||||
|
<div className="absolute z-50 mt-1 w-full max-w-2xl bg-white shadow-lg rounded-md p-3 border border-gray-100">
|
||||||
|
<ContactModal
|
||||||
|
contactData={contactData}
|
||||||
|
inpFirstName={recipient.firstName}
|
||||||
|
inpLastName={recipient.lastName}
|
||||||
|
inpEmail={recipient.email}
|
||||||
|
openContactModal={openContactModal}
|
||||||
|
setRecipients={setRecipients}
|
||||||
|
setOpenContactModal={setOpenContactModal}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Signing options + Add button in one line */}
|
||||||
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||||
|
<div className="flex flex-wrap gap-6">
|
||||||
|
{[
|
||||||
|
{ label: "Signer", value: recipientType.SIGNER },
|
||||||
|
{ label: "In Person Signer", value: recipientType.INPERSONSIGNER },
|
||||||
|
{ label: "CC", value: recipientType.CC },
|
||||||
|
].map((option) => (
|
||||||
|
<label key={option.value} className="flex items-center gap-2 text-gray-700 text-sm cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="recipient-type"
|
||||||
|
value={option.value}
|
||||||
|
defaultChecked={option.value === recipientType.SIGNER}
|
||||||
|
onClick={(e) => setRecipients({ ...recipient, type: e.target.value })}
|
||||||
|
className="accent-[var(--primary)] cursor-pointer"
|
||||||
|
/>
|
||||||
|
{option.label}
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="inline-flex items-center gap-2 bg-[var(--primary)] hover:bg-[var(--primaryLight2)] text-white text-sm font-semibold px-5 py-2 rounded-md transition"
|
||||||
|
>
|
||||||
|
<FaPlus size={14} /> Add Recipient
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ const RecipientCards = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const recipients = useSelector((state: RootState) => state.selectedRecipients.recipients);
|
const recipients = useSelector((state: RootState) => state.selectedRecipients.recipients);
|
||||||
|
console.log("recipients.length",recipients)
|
||||||
// const signingOrder = useSelector((state: RootState) => state.selectedRecipients.signingOrder);
|
// const signingOrder = useSelector((state: RootState) => state.selectedRecipients.signingOrder);
|
||||||
const signType = useSelector((state: RootState) => state.selectedDocuments.signType);
|
const signType = useSelector((state: RootState) => state.selectedDocuments.signType);
|
||||||
|
|
||||||
|
|
@ -137,7 +138,7 @@ const RecipientCards = () => {
|
||||||
{recipient.firstName} {recipient.lastName || ""}
|
{recipient.firstName} {recipient.lastName || ""}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-500 truncate">
|
<p className="text-xs text-gray-500 truncate">
|
||||||
{recipient.email} | {recipient.type}
|
{recipient.email} | {recipient.type} {recipient.roleName ? `| Role : ${recipient.roleName}` : ""}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useState, useRef } from 'react';
|
import { useState, useRef, useEffect } from 'react';
|
||||||
import { ReactSortable } from 'react-sortablejs';
|
import { ReactSortable } from 'react-sortablejs';
|
||||||
import { RiDraggable } from "react-icons/ri";
|
import { RiDraggable } from "react-icons/ri";
|
||||||
import { FaFileAlt, FaPlus, FaGoogleDrive, FaDropbox, FaMicrosoft, FaCloudUploadAlt } from "react-icons/fa";
|
import { FaFileAlt, FaPlus, FaGoogleDrive, FaDropbox, FaMicrosoft, FaCloudUploadAlt } from "react-icons/fa";
|
||||||
|
|
@ -13,10 +13,21 @@ import DocumentCard from './DocumentCard';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice';
|
import { toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice';
|
||||||
|
|
||||||
const SelectedDocuments = () => {
|
const SelectedDocuments = ({transactionTemplateToggle}) => {
|
||||||
// let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS;
|
// let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS;
|
||||||
|
|
||||||
|
// const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
// await sleep(5000);
|
||||||
|
|
||||||
const documents = useSelector((state: RootState) => state.documents.documents);
|
const documents = useSelector((state: RootState) => state.documents.documents);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
console.log("Documents updated:", documents);
|
||||||
|
}, [documents]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const templateId = useParams<{ templateId: string }>().templateId;
|
const templateId = useParams<{ templateId: string }>().templateId;
|
||||||
let { accountId, transactionId } = useParams<{ accountId: string, transactionId: string }>();
|
let { accountId, transactionId } = useParams<{ accountId: string, transactionId: string }>();
|
||||||
|
|
||||||
|
|
@ -60,6 +71,7 @@ const SelectedDocuments = () => {
|
||||||
formData.append("file", filesToUpload[0])
|
formData.append("file", filesToUpload[0])
|
||||||
console.log(formData, "Line 59")
|
console.log(formData, "Line 59")
|
||||||
// filesToUpload.forEach((file) => formData.append("document", file));
|
// filesToUpload.forEach((file) => formData.append("document", file));
|
||||||
|
|
||||||
dispatch(uploadDocuments({ formData, accountId, transactionId }))
|
dispatch(uploadDocuments({ formData, accountId, transactionId }))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
|
@ -79,6 +91,7 @@ const SelectedDocuments = () => {
|
||||||
// Update selected documents after upload
|
// Update selected documents after upload
|
||||||
// dispatch(setSelectedDocuments([...documents, ...res]));
|
// dispatch(setSelectedDocuments([...documents, ...res]));
|
||||||
|
|
||||||
|
// debugger;
|
||||||
setFiles([]);
|
setFiles([]);
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
})
|
})
|
||||||
|
|
@ -102,7 +115,7 @@ const SelectedDocuments = () => {
|
||||||
disabled={false}
|
disabled={false}
|
||||||
>
|
>
|
||||||
{documents.map((doc) => (
|
{documents.map((doc) => (
|
||||||
<DocumentCard key={doc.id} id={doc.id} name={doc.name} isCompleted={("isCompleted" in doc || ("status" in doc && doc.status === "UPLOADED")) ? true : false} />
|
<DocumentCard key={doc.id} id={doc.id} name={doc.name} isCompleted={("isCompleted" in doc || ("status" in doc && doc.status === "UPLOADED")) ? true : false} transactionTemplateToggle={transactionTemplateToggle}/>
|
||||||
))}
|
))}
|
||||||
</ReactSortable>
|
</ReactSortable>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,14 @@ import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { IoMdClose } from 'react-icons/io';
|
import { IoMdClose } from 'react-icons/io';
|
||||||
|
|
||||||
import { fetchRecipients } from '../../../store/slices/selectedRecipientsSlice';
|
import { fetchRecipients, toggleToTemplates } from '../../../store/slices/selectedRecipientsSlice';
|
||||||
import { fetchDocuments } from '../../../store/slices/documentSlice';
|
import { fetchDocuments } from '../../../store/slices/documentSlice';
|
||||||
import { FaQuestionCircle } from 'react-icons/fa';
|
import { FaQuestionCircle } from 'react-icons/fa';
|
||||||
import typeToIconMap from '../../../helper/iconMap';
|
import typeToIconMap from '../../../helper/iconMap';
|
||||||
|
|
||||||
import { IoAdd } from 'react-icons/io5';
|
import { IoAdd } from 'react-icons/io5';
|
||||||
import Loader from '../../dashboard/Loader';
|
import Loader from '../../dashboard/Loader';
|
||||||
|
import { TRANSACTION_TEMPLATE_TOGGLE } from "../../../store/slices/selectedRecipientsSlice";
|
||||||
|
|
||||||
export interface DroppedItemsObj {
|
export interface DroppedItemsObj {
|
||||||
[key: string]: DroppedItem[]
|
[key: string]: DroppedItem[]
|
||||||
|
|
@ -41,7 +42,16 @@ interface DocumentContainerProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
|
const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
|
||||||
const { accountId, transactionId } = useParams<{ accountId: string, transactionId: string }>();
|
let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS;
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const params = useParams<{ accountId?: string; transactionId?: string; templateId?: string }>();
|
||||||
|
const accountId = params.accountId;
|
||||||
|
let transactionId = params.transactionId
|
||||||
|
if (!transactionId) {
|
||||||
|
transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES;
|
||||||
|
transactionId = params.templateId;
|
||||||
|
dispatch(toggleToTemplates());
|
||||||
|
}
|
||||||
if (!accountId || !transactionId) {
|
if (!accountId || !transactionId) {
|
||||||
window.location.href = "/";
|
window.location.href = "/";
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -49,7 +59,6 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
|
||||||
|
|
||||||
// const transactionId = useSelector((state: RootState) => state.transaction.transactionId)
|
// const transactionId = useSelector((state: RootState) => state.transaction.transactionId)
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients);
|
const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients);
|
||||||
// const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments)
|
// const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments)
|
||||||
|
|
@ -67,7 +76,7 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
|
||||||
const fetchTags = async () => {
|
const fetchTags = async () => {
|
||||||
try {
|
try {
|
||||||
const requests = selectedRecipients.map((recipient) =>
|
const requests = selectedRecipients.map((recipient) =>
|
||||||
api.get(`/api/accounts/${accountId}/transactions/${transactionId}/recipients/${recipient.id}/tags`)
|
api.get(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}/recipients/${recipient.id}/tags`)
|
||||||
);
|
);
|
||||||
const response = await Promise.all(requests);
|
const response = await Promise.all(requests);
|
||||||
let recTags = {}
|
let recTags = {}
|
||||||
|
|
@ -151,7 +160,7 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
|
||||||
|
|
||||||
const requests = Object.entries(updatedData).map(([recipientId, tags]) => {
|
const requests = Object.entries(updatedData).map(([recipientId, tags]) => {
|
||||||
return api.put(
|
return api.put(
|
||||||
`/api/accounts/${accountId}/transactions/${transactionId}/recipients/${recipientId}/tags`,
|
`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}/recipients/${recipientId}/tags`,
|
||||||
tags
|
tags
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -279,7 +288,7 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
|
||||||
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}/${transactionTemplateToggle}/${transactionId}`, { status: "SENT" })
|
||||||
console.log(res, "Check123")
|
console.log(res, "Check123")
|
||||||
})()
|
})()
|
||||||
}, [isSend])
|
}, [isSend])
|
||||||
|
|
@ -507,7 +516,8 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
|
||||||
(async () => {
|
(async () => {
|
||||||
if ("id" in tag) {
|
if ("id" in tag) {
|
||||||
try {
|
try {
|
||||||
const response = await api.delete(`/api/accounts/${accountId}/transactions/${transactionId}/recipients/${recipientId}/tags/${tag.id}`)
|
|
||||||
|
const response = await api.delete(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}/recipients/${recipientId}/tags/${tag.id}`)
|
||||||
if (response.status !== 200) throw "Tag Deletion error"
|
if (response.status !== 200) throw "Tag Deletion error"
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Tag Deletion error")
|
console.error("Tag Deletion error")
|
||||||
|
|
@ -524,7 +534,7 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
|
||||||
})()
|
})()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAddRadioClick = (key:string) => {
|
const handleAddRadioClick = (key: string) => {
|
||||||
console.log("Handle Radio Clicled", key)
|
console.log("Handle Radio Clicled", key)
|
||||||
console.log(droppedItems, "line 525")
|
console.log(droppedItems, "line 525")
|
||||||
}
|
}
|
||||||
|
|
@ -844,11 +854,11 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{selectedTag && <RightSideBar selectedTag={droppedItems[selectedTag.key][selectedTag.index]} setDroppedItems={setDroppedItems} selTagKey={selectedTag.key} selTagIndx={selectedTag.index} DirectionalControlsProps={{
|
{selectedTag && <RightSideBar selectedTag={droppedItems[selectedTag.key][selectedTag.index]} setDroppedItems={setDroppedItems} selTagKey={selectedTag.key} selTagIndx={selectedTag.index} DirectionalControlsProps={{
|
||||||
onMove: directionalControlsHandler,
|
onMove: directionalControlsHandler,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
stepSize: 5,
|
stepSize: 5,
|
||||||
className: ''
|
className: ''
|
||||||
}} />}
|
}} />}
|
||||||
{/* Center Doc Container - END */}
|
{/* Center Doc Container - END */}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { RootState } from '../../../store/store';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import FieldList from './FieldList';
|
import FieldList from './FieldList';
|
||||||
|
|
||||||
import { addColorsToRecipient, fetchRecipients } from '../../../store/slices/selectedRecipientsSlice';
|
import { addColorsToRecipient, fetchRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice';
|
||||||
|
|
||||||
import { IoCheckmark } from 'react-icons/io5'
|
import { IoCheckmark } from 'react-icons/io5'
|
||||||
import { MdKeyboardArrowDown } from 'react-icons/md'
|
import { MdKeyboardArrowDown } from 'react-icons/md'
|
||||||
|
|
@ -17,13 +17,21 @@ interface LeftSideBarProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, selectedRecipientIndx, setSelectedRecipient }) => {
|
const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, selectedRecipientIndx, setSelectedRecipient }) => {
|
||||||
const { accountId, transactionId } = useParams<{ accountId: string, transactionId: string }>();
|
let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS;
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const params = useParams<{ accountId?: string; transactionId?: string; templateId?: string }>();
|
||||||
|
const accountId = params.accountId;
|
||||||
|
let transactionId = params.transactionId
|
||||||
|
if (!transactionId) {
|
||||||
|
transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES;
|
||||||
|
transactionId = params.templateId;
|
||||||
|
dispatch(toggleToTemplates());
|
||||||
|
}
|
||||||
if (!accountId || !transactionId) {
|
if (!accountId || !transactionId) {
|
||||||
window.location.href = "/";
|
window.location.href = "/";
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients)
|
const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients)
|
||||||
const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
|
const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
|
||||||
|
|
@ -88,7 +96,7 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, selectedRecipientIn
|
||||||
setDropDownToggle(false);
|
setDropDownToggle(false);
|
||||||
}, 0)
|
}, 0)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className='flex items-center gap-1.5'>
|
<div className='flex items-center gap-1.5'>
|
||||||
<p className='w-3 h-3 rounded-sm' style={{ background: recipient.color }}></p>
|
<p className='w-3 h-3 rounded-sm' style={{ background: recipient.color }}></p>
|
||||||
{/* <FaUser className='w-3 h-3 text-2xl mt-[1px]' style={{ color: recipient.color }} /> */}
|
{/* <FaUser className='w-3 h-3 text-2xl mt-[1px]' style={{ color: recipient.color }} /> */}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import { IoIosDocument } from 'react-icons/io'
|
||||||
import { LuTableOfContents } from 'react-icons/lu'
|
import { LuTableOfContents } from 'react-icons/lu'
|
||||||
import { MdKeyboardArrowDown } from 'react-icons/md'
|
import { MdKeyboardArrowDown } from 'react-icons/md'
|
||||||
import { RiResetLeftLine, RiResetRightLine } from 'react-icons/ri'
|
import { RiResetLeftLine, RiResetRightLine } from 'react-icons/ri'
|
||||||
import { fetchRecipients } from '../../../store/slices/selectedRecipientsSlice'
|
import { fetchRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice'
|
||||||
import { fetchDocuments } from '../../../store/slices/documentSlice'
|
import { fetchDocuments } from '../../../store/slices/documentSlice'
|
||||||
import { useNavigate, useParams } from 'react-router-dom'
|
import { useNavigate, useParams } from 'react-router-dom'
|
||||||
|
|
||||||
|
|
@ -45,7 +45,16 @@ const fitObj = [
|
||||||
]
|
]
|
||||||
|
|
||||||
const NavTwo: React.FC<NavTwoProps> = ({ isFields, setIsFields, isSend, setIsSend }) => {
|
const NavTwo: React.FC<NavTwoProps> = ({ isFields, setIsFields, isSend, setIsSend }) => {
|
||||||
const { accountId, transactionId } = useParams<{ accountId: string, transactionId: string }>();
|
let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS;
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const params = useParams<{ accountId?: string; transactionId?: string; templateId?: string }>();
|
||||||
|
const accountId = params.accountId;
|
||||||
|
let transactionId = params.transactionId
|
||||||
|
if (!transactionId) {
|
||||||
|
transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES;
|
||||||
|
transactionId = params.templateId;
|
||||||
|
dispatch(toggleToTemplates());
|
||||||
|
}
|
||||||
if (!accountId || !transactionId) {
|
if (!accountId || !transactionId) {
|
||||||
window.location.href = "/";
|
window.location.href = "/";
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -70,8 +79,6 @@ const NavTwo: React.FC<NavTwoProps> = ({ isFields, setIsFields, isSend, setIsSen
|
||||||
const [nav2RecToggle, setNav2RecToggle] = useState<boolean>(false);
|
const [nav2RecToggle, setNav2RecToggle] = useState<boolean>(false);
|
||||||
const [nav2DocToggle, setNav2DocToggle] = useState<boolean>(false);
|
const [nav2DocToggle, setNav2DocToggle] = useState<boolean>(false);
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
function handleSend() {
|
function handleSend() {
|
||||||
// dispatch(showLoader("Loading ..."));
|
// dispatch(showLoader("Loading ..."));
|
||||||
if (isSend === null) {
|
if (isSend === null) {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import Loader from '../../dashboard/Loader';
|
||||||
import { hideLoader } from '../../../store/slices/loaderSlice';
|
import { hideLoader } from '../../../store/slices/loaderSlice';
|
||||||
|
|
||||||
const ProcessStepTwo = () => {
|
const ProcessStepTwo = () => {
|
||||||
|
|
||||||
const [isFields, setIsFields] = useState(true);
|
const [isFields, setIsFields] = useState(true);
|
||||||
const [selectedRecipient, setSelectedRecipient] = useState(0);
|
const [selectedRecipient, setSelectedRecipient] = useState(0);
|
||||||
const [isSend, setIsSend] = useState<boolean | null>(null);
|
const [isSend, setIsSend] = useState<boolean | null>(null);
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,26 @@
|
||||||
import { useState } from "react";
|
import { use, useEffect, useState } from "react";
|
||||||
import { PiDotsThreeOutlineVerticalFill } from "react-icons/pi";
|
import { PiDotsThreeOutlineVerticalFill } from "react-icons/pi";
|
||||||
import { Bounce, toast } from "react-toastify";
|
import { Bounce, toast } from "react-toastify";
|
||||||
import api from "../../config/api";
|
import api from "../../config/api";
|
||||||
import { IoMdAdd } from "react-icons/io";
|
import { IoMdAdd } from "react-icons/io";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
|
import { FaSpinner } from "react-icons/fa";
|
||||||
|
import LoaderUi from "../../helper/LoaderUi";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const Templates = () => {
|
const Templates = () => {
|
||||||
const [activeFilter, setActiveFilter] = useState("All");
|
const [activeFilter, setActiveFilter] = useState("All");
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [templates, setTemplates] = useState([])
|
||||||
const accountDetails = useSelector((state: any) => state.user.account);
|
const accountDetails = useSelector((state: any) => state.user.account);
|
||||||
const accountId = accountDetails.id || "";
|
const accountId = accountDetails.id || "";
|
||||||
|
const [showMenu, setShowMenu] = useState(null);
|
||||||
|
|
||||||
|
const [activeAccountId, setActiveAccountId] = useState<string | null>(
|
||||||
|
JSON.parse(sessionStorage.getItem('activeAccountId') || 'null')
|
||||||
|
);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
|
@ -18,11 +28,45 @@ const Templates = () => {
|
||||||
|
|
||||||
const handleAddTemplate = async () => {
|
const handleAddTemplate = async () => {
|
||||||
console.log("handleAddTemplate called");
|
console.log("handleAddTemplate called");
|
||||||
const res = await api.post(`/api/accounts/${accountId}/templates`);
|
try {
|
||||||
|
const res = await api.post(`/api/accounts/${activeAccountId}/templates`);
|
||||||
|
navigate(`/process-step-one/${activeAccountId}/template/${res.data}`)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
|
||||||
navigate(`/process-step-one/${accountId}/template/${res.data}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getAllTemplates = async () => {
|
||||||
|
setLoading(true)
|
||||||
|
try {
|
||||||
|
let res = await api.get(`/api/accounts/${activeAccountId}/templates`)
|
||||||
|
let templateData = res?.data
|
||||||
|
setTemplates(templateData)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeAccountId) {
|
||||||
|
getAllTemplates()
|
||||||
|
}
|
||||||
|
}, [activeAccountId])
|
||||||
|
|
||||||
|
|
||||||
|
const formatDate = (timestamp: number) => {
|
||||||
|
return new Date(timestamp * 1000).toLocaleString();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUseFun = (id) => {
|
||||||
|
alert(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-3 text-sm">
|
<div className="p-3 text-sm">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
|
|
@ -56,42 +100,109 @@ const Templates = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Table */}
|
{/* Table */}
|
||||||
<div className="overflow-x-auto">
|
<div className="max-h-[77vh] pb-16 overflow-y-auto relative no-scrollbar">
|
||||||
<table className="min-w-full border border-gray-200 bg-white shadow rounded">
|
<table className="min-w-full border border-gray-200 bg-white shadow rounded table-fixed">
|
||||||
<thead className="bg-gray-100 text-xs text-gray-700 uppercase tracking-wider">
|
<thead className="bg-gray-100 text-xs text-gray-700 uppercase tracking-wider sticky top-0 z-10">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-4 py-2 w-10"></th>
|
<th className="px-4 py-3 w-10 bg-gray-100">
|
||||||
<th className="px-4 py-2 text-left">Name</th>
|
|
||||||
<th className="px-4 py-2 text-center">Owner</th>
|
|
||||||
<th className="px-4 py-2 text-center">Created</th>
|
|
||||||
<th className="px-4 py-2 text-center">Last Updated</th>
|
|
||||||
<th className="px-4 py-2 text-center"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody className="divide-y divide-gray-200">
|
|
||||||
<tr className="hover:bg-gray-50">
|
|
||||||
<td className="px-4 py-2">
|
|
||||||
<div className="w-4 h-4 border border-slate-300 rounded flex items-center justify-center">
|
<div className="w-4 h-4 border border-slate-300 rounded flex items-center justify-center">
|
||||||
{/* <FaCheck className="text-purple-600 text-xs" /> */}
|
{/* <FaCheck className="text-purple-600 text-xs" /> */}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</th>
|
||||||
<td className="px-4 py-2 text-sm">Test Template</td>
|
<th className="px-4 py-2 text-center bg-gray-100">Name</th>
|
||||||
<td className="px-4 py-2 text-center text-sm">User 1</td>
|
<th className="px-4 py-2 text-center bg-gray-100">Created</th>
|
||||||
<td className="px-4 py-2 text-center text-sm">2025-07-14</td>
|
<th className="px-4 py-2 text-center bg-gray-100">Last Updated</th>
|
||||||
<td className="px-4 py-2 text-center text-sm">Just now</td>
|
<th className="px-4 py-2 text-center bg-gray-100">Use</th>
|
||||||
<td className="px-4 py-2 text-center relative">
|
<th className="px-4 py-2 text-center bg-gray-100">Action</th>
|
||||||
<button
|
|
||||||
className="text-slate-400 hover:text-black"
|
|
||||||
>
|
|
||||||
<PiDotsThreeOutlineVerticalFill className="text-lg" />
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</thead>
|
||||||
|
|
||||||
|
{templates.length > 0 ? (
|
||||||
|
<tbody className="divide-y divide-gray-200">
|
||||||
|
{templates.map((item, index) => {
|
||||||
|
return (
|
||||||
|
<tr key={index} className="hover:bg-gray-50 relative">
|
||||||
|
<td className="px-4 py-2">
|
||||||
|
<div className="w-4 h-4 border border-slate-300 rounded flex items-center justify-center">
|
||||||
|
{/* <FaCheck className="text-purple-600 text-xs" /> */}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-center">{item.name || 'Dummy'}</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-center">{formatDate(item.createdAt)}</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-center">{formatDate(item.lastModifiedAt)}</td>
|
||||||
|
<td className="px-4 py-2 text-sm text-center" onClick={() => handleUseFun(item.id)}><span className="from-green-500 cursor-pointer to-blue-500 bg-gradient-to-br px-3 py-1 rounded-lg text-white font-semibold text-[12px]">Use</span></td>
|
||||||
|
{/* Dropdown Trigger + Tooltip */}
|
||||||
|
<td className="px-4 py-2 text-center relative">
|
||||||
|
<button
|
||||||
|
onClick={() => setShowMenu(showMenu === index ? null : index)}
|
||||||
|
className="text-slate-400 hover:text-black focus:outline-none"
|
||||||
|
>
|
||||||
|
<PiDotsThreeOutlineVerticalFill className="text-lg" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{showMenu === index && (
|
||||||
|
<div
|
||||||
|
className="absolute right-15 top-5 mt-2 w-32 bg-white border border-gray-200 rounded shadow-lg z-10 transition-opacity duration-300 ease-in-out"
|
||||||
|
tabIndex={0} // Make it focusable
|
||||||
|
onBlur={(e) => {
|
||||||
|
// Ensure it only closes if focus truly leaves the container
|
||||||
|
if (!e.currentTarget.contains(e.relatedTarget)) {
|
||||||
|
setShowMenu(null);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
|
||||||
|
onClick={() => {
|
||||||
|
setShowMenu(null);
|
||||||
|
navigate(`/process-step-two/${accountId}/template/${item.id}`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="block w-full text-left px-4 py-2 text-sm text-red-600 hover:bg-red-100"
|
||||||
|
onClick={() => {
|
||||||
|
setShowMenu(null);
|
||||||
|
console.log('Delete clicked', item);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
) : (
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colSpan="5">
|
||||||
|
<div className="relative flex justify-center items-center h-100 w-full">
|
||||||
|
{/* Pulsing background div */}
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-r from-gray-200 to-white animate-pulse"></div>
|
||||||
|
|
||||||
|
{/* Loader stays on top, no animation */}
|
||||||
|
<LoaderUi classStyle="relative h-16 w-16" />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
)}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Templates;
|
export default Templates;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ api.interceptors.response.use(
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
async error => {
|
async error => {
|
||||||
|
|
||||||
const originalRequest = error.config;
|
const originalRequest = error.config;
|
||||||
// Prevent multiple refresh attempts
|
// Prevent multiple refresh attempts
|
||||||
// if (error.response?.status === 401 || error.response?.status === 500) {
|
// if (error.response?.status === 401 || error.response?.status === 500) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
interface StyleProps {
|
||||||
|
classStyle : string
|
||||||
|
}
|
||||||
|
|
||||||
|
const LoaderUi = ({ classStyle }:StyleProps) => {
|
||||||
|
return (
|
||||||
|
<div className={classStyle}>
|
||||||
|
<svg viewBox="0 0 240 240" height="240" width="240" className="pl">
|
||||||
|
<circle strokeLinecap="round" strokeDashoffset="-330" strokeDasharray="0 660" strokeWidth="20" stroke="#000" fill="none" r="105" cy="120" cx="120" className="pl__ring pl__ring--a"></circle>
|
||||||
|
<circle strokeLinecap="round" strokeDashoffset="-110" strokeDasharray="0 220" strokeWidth="20" stroke="#000" fill="none" r="35" cy="120" cx="120" className="pl__ring pl__ring--b"></circle>
|
||||||
|
<circle strokeLinecap="round" strokeDasharray="0 440" strokeWidth="20" stroke="#000" fill="none" r="70" cy="120" cx="85" className="pl__ring pl__ring--c"></circle>
|
||||||
|
<circle strokeLinecap="round" strokeDasharray="0 440" strokeWidth="20" stroke="#000" fill="none" r="70" cy="120" cx="155" className="pl__ring pl__ring--d"></circle>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LoaderUi
|
||||||
206
src/index.css
206
src/index.css
|
|
@ -94,4 +94,208 @@ canvas {
|
||||||
|
|
||||||
|
|
||||||
/* Loader CSS */
|
/* Loader CSS */
|
||||||
|
/* From Uiverse.io by Juanes200122 */
|
||||||
|
.pl {
|
||||||
|
width: 6em;
|
||||||
|
height: 6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl__ring {
|
||||||
|
animation: ringA 2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl__ring--a {
|
||||||
|
stroke: #41644A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl__ring--b {
|
||||||
|
animation-name: ringB;
|
||||||
|
stroke: #7e7e7e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl__ring--c {
|
||||||
|
animation-name: ringC;
|
||||||
|
stroke: #686868;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl__ring--d {
|
||||||
|
animation-name: ringD;
|
||||||
|
stroke: #41644A;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
@keyframes ringA {
|
||||||
|
from, 4% {
|
||||||
|
stroke-dasharray: 0 660;
|
||||||
|
stroke-width: 20;
|
||||||
|
stroke-dashoffset: -330;
|
||||||
|
}
|
||||||
|
|
||||||
|
12% {
|
||||||
|
stroke-dasharray: 60 600;
|
||||||
|
stroke-width: 30;
|
||||||
|
stroke-dashoffset: -335;
|
||||||
|
}
|
||||||
|
|
||||||
|
32% {
|
||||||
|
stroke-dasharray: 60 600;
|
||||||
|
stroke-width: 30;
|
||||||
|
stroke-dashoffset: -595;
|
||||||
|
}
|
||||||
|
|
||||||
|
40%, 54% {
|
||||||
|
stroke-dasharray: 0 660;
|
||||||
|
stroke-width: 20;
|
||||||
|
stroke-dashoffset: -660;
|
||||||
|
}
|
||||||
|
|
||||||
|
62% {
|
||||||
|
stroke-dasharray: 60 600;
|
||||||
|
stroke-width: 30;
|
||||||
|
stroke-dashoffset: -665;
|
||||||
|
}
|
||||||
|
|
||||||
|
82% {
|
||||||
|
stroke-dasharray: 60 600;
|
||||||
|
stroke-width: 30;
|
||||||
|
stroke-dashoffset: -925;
|
||||||
|
}
|
||||||
|
|
||||||
|
90%, to {
|
||||||
|
stroke-dasharray: 0 660;
|
||||||
|
stroke-width: 20;
|
||||||
|
stroke-dashoffset: -990;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ringB {
|
||||||
|
from, 12% {
|
||||||
|
stroke-dasharray: 0 220;
|
||||||
|
stroke-width: 20;
|
||||||
|
stroke-dashoffset: -110;
|
||||||
|
}
|
||||||
|
|
||||||
|
20% {
|
||||||
|
stroke-dasharray: 20 200;
|
||||||
|
stroke-width: 30;
|
||||||
|
stroke-dashoffset: -115;
|
||||||
|
}
|
||||||
|
|
||||||
|
40% {
|
||||||
|
stroke-dasharray: 20 200;
|
||||||
|
stroke-width: 30;
|
||||||
|
stroke-dashoffset: -195;
|
||||||
|
}
|
||||||
|
|
||||||
|
48%, 62% {
|
||||||
|
stroke-dasharray: 0 220;
|
||||||
|
stroke-width: 20;
|
||||||
|
stroke-dashoffset: -220;
|
||||||
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
stroke-dasharray: 20 200;
|
||||||
|
stroke-width: 30;
|
||||||
|
stroke-dashoffset: -225;
|
||||||
|
}
|
||||||
|
|
||||||
|
90% {
|
||||||
|
stroke-dasharray: 20 200;
|
||||||
|
stroke-width: 30;
|
||||||
|
stroke-dashoffset: -305;
|
||||||
|
}
|
||||||
|
|
||||||
|
98%, to {
|
||||||
|
stroke-dasharray: 0 220;
|
||||||
|
stroke-width: 20;
|
||||||
|
stroke-dashoffset: -330;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ringC {
|
||||||
|
from {
|
||||||
|
stroke-dasharray: 0 440;
|
||||||
|
stroke-width: 20;
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
8% {
|
||||||
|
stroke-dasharray: 40 400;
|
||||||
|
stroke-width: 30;
|
||||||
|
stroke-dashoffset: -5;
|
||||||
|
}
|
||||||
|
|
||||||
|
28% {
|
||||||
|
stroke-dasharray: 40 400;
|
||||||
|
stroke-width: 30;
|
||||||
|
stroke-dashoffset: -175;
|
||||||
|
}
|
||||||
|
|
||||||
|
36%, 58% {
|
||||||
|
stroke-dasharray: 0 440;
|
||||||
|
stroke-width: 20;
|
||||||
|
stroke-dashoffset: -220;
|
||||||
|
}
|
||||||
|
|
||||||
|
66% {
|
||||||
|
stroke-dasharray: 40 400;
|
||||||
|
stroke-width: 30;
|
||||||
|
stroke-dashoffset: -225;
|
||||||
|
}
|
||||||
|
|
||||||
|
86% {
|
||||||
|
stroke-dasharray: 40 400;
|
||||||
|
stroke-width: 30;
|
||||||
|
stroke-dashoffset: -395;
|
||||||
|
}
|
||||||
|
|
||||||
|
94%, to {
|
||||||
|
stroke-dasharray: 0 440;
|
||||||
|
stroke-width: 20;
|
||||||
|
stroke-dashoffset: -440;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ringD {
|
||||||
|
from, 8% {
|
||||||
|
stroke-dasharray: 0 440;
|
||||||
|
stroke-width: 20;
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
16% {
|
||||||
|
stroke-dasharray: 40 400;
|
||||||
|
stroke-width: 30;
|
||||||
|
stroke-dashoffset: -5;
|
||||||
|
}
|
||||||
|
|
||||||
|
36% {
|
||||||
|
stroke-dasharray: 40 400;
|
||||||
|
stroke-width: 30;
|
||||||
|
stroke-dashoffset: -175;
|
||||||
|
}
|
||||||
|
|
||||||
|
44%, 50% {
|
||||||
|
stroke-dasharray: 0 440;
|
||||||
|
stroke-width: 20;
|
||||||
|
stroke-dashoffset: -220;
|
||||||
|
}
|
||||||
|
|
||||||
|
58% {
|
||||||
|
stroke-dasharray: 40 400;
|
||||||
|
stroke-width: 30;
|
||||||
|
stroke-dashoffset: -225;
|
||||||
|
}
|
||||||
|
|
||||||
|
78% {
|
||||||
|
stroke-dasharray: 40 400;
|
||||||
|
stroke-width: 30;
|
||||||
|
stroke-dashoffset: -395;
|
||||||
|
}
|
||||||
|
|
||||||
|
86%, to {
|
||||||
|
stroke-dasharray: 0 440;
|
||||||
|
stroke-width: 20;
|
||||||
|
stroke-dashoffset: -440;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -223,6 +223,7 @@ export const fetchDocuments = createAsyncThunk<
|
||||||
// -----------------------
|
// -----------------------
|
||||||
// Thunk: Upload Documents
|
// Thunk: Upload Documents
|
||||||
// -----------------------
|
// -----------------------
|
||||||
|
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
export const uploadDocuments = createAsyncThunk<
|
export const uploadDocuments = createAsyncThunk<
|
||||||
Document[],
|
Document[],
|
||||||
{ formData: FormData; accountId: string; transactionId: string }
|
{ formData: FormData; accountId: string; transactionId: string }
|
||||||
|
|
@ -237,6 +238,10 @@ export const uploadDocuments = createAsyncThunk<
|
||||||
};
|
};
|
||||||
const toggle = state.selectedRecipients.transactionTemplateToggle;
|
const toggle = state.selectedRecipients.transactionTemplateToggle;
|
||||||
|
|
||||||
|
console.log(`/api/accounts/${accountId}/${toggle}/${transactionId}/documents`)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const response = await api.post(
|
const response = await api.post(
|
||||||
`/api/accounts/${accountId}/${toggle}/${transactionId}/documents`,
|
`/api/accounts/${accountId}/${toggle}/${transactionId}/documents`,
|
||||||
formData,
|
formData,
|
||||||
|
|
@ -246,7 +251,8 @@ export const uploadDocuments = createAsyncThunk<
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
console.log("// await sleep(5000);", response.data)
|
||||||
|
// await sleep(5000);
|
||||||
dispatch(hideLoader());
|
dispatch(hideLoader());
|
||||||
return [response.data];
|
return [response.data];
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
|
||||||
|
|
@ -351,6 +351,7 @@ export interface Recipient {
|
||||||
hostEmail: string;
|
hostEmail: string;
|
||||||
type: recipientType;
|
type: recipientType;
|
||||||
color?: string;
|
color?: string;
|
||||||
|
roleName:string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SelectedRecipientInt {
|
export interface SelectedRecipientInt {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue