Compare commits

...
23 Commits
Author SHA1 Message Date
AndrewandGitHub d4be4aec2c Merge pull request #143 from rishabjasrotia/issue/81 2023-11-07 18:09:08 +05:30
RishabandGitHub 4caf7b8c1a Update header.js 2023-11-07 17:31:50 +05:30
AmolandGitHub c736bbce55 Merge pull request #145 from prafull-opensignlabs/staging
remove hardcoded pfx passphrase
2023-11-07 16:30:35 +05:30
prafull-opensignlabsandGitHub 4bbf0d05d5 Merge pull request #147 from lsprr/accessibility/login
Improve accessibility of the login form
2023-11-07 16:14:40 +05:30
prafull-opensignlabs f227b79316 change prod and dev command 2023-11-07 11:29:08 +05:30
lsprr d4b4e02b62 Improve accessibility of the login form
- Add ARIA labels to input fields for better screen reader support
- Implement keyboard navigation for the login form elements
- Fix semantic HTML issues for assistive technologies
2023-11-06 15:57:49 -05:00
prafull-opensignlabs 1e76cf5253 fix: login url in mail change to current host 2023-11-06 20:48:01 +05:30
prafull-opensignlabs 4b9088b252 docs: update pass_phrase variable in env files 2023-11-06 20:00:10 +05:30
prafull-opensignlabs 820d6378f9 fix: add loadmf route to load login flow of microapp, add pagenotfound microapp 2023-11-06 19:44:06 +05:30
prafull-opensignlabs dfc816bdac Merge branch 'staging' of https://github.com/prafull-opensignlabs/OpenSign into staging 2023-11-06 16:51:17 +05:30
prafull-opensignlabs 0f62419d99 remove confidential passphrase param 2023-11-06 16:48:06 +05:30
prafull-opensignlabsandGitHub 37af995aaa Merge pull request #8 from OpenSignLabs/staging
sync fork
2023-11-06 16:10:54 +05:30
RishabandGitHub 4c69971ba1 Update emailComponent.js 2023-11-06 14:52:23 +05:30
RishabandGitHub 9047aa7dc8 Fix Printing for mobile 2023-11-06 14:49:44 +05:30
RishabandGitHub d33df9b934 Fix Print for mobile 2023-11-06 14:49:25 +05:30
AndrewandGitHub 3164ee5824 Merge pull request #142 from OpenSignLabs/all-contributors/add-lsprr
docs: add lsprr as a contributor for a11y
2023-11-06 13:56:42 +05:30
allcontributors[bot]andGitHub 6a98adebd2 docs: update .all-contributorsrc 2023-11-06 08:26:13 +00:00
allcontributors[bot]andGitHub 015b752f5b docs: update README.md 2023-11-06 08:26:12 +00:00
AndrewandGitHub 11164a716f Merge pull request #140 from lsprr/lsprr/accessibility/sidebar
Refactor Menu and Social Media Components for Improved Accessibility
2023-11-06 13:55:19 +05:30
AndrewandGitHub 64638acd43 Merge pull request #141 from prafull-opensignlabs/staging 2023-11-06 13:24:20 +05:30
prafull-opensignlabsandGitHub 133769d5fd Merge pull request #7 from OpenSignLabs/staging
Merge pull request #137 from prafull-opensignlabs/staging
2023-11-06 13:19:11 +05:30
prafull-opensignlabs e330285e24 add pagination in reports, fix navigation in signup 2023-11-06 13:17:01 +05:30
lsprr 2acf851852 Refactor: Split Sidebar into Menu and SocialMedia components for modularity
- Created separate Menu and SocialMedia components from the Sidebar.
- Enhanced accessibility features in Menu.js, Sidebar.js, SubMenu.js, and SocialMedia.js.
- Adjusted NavLink components to improve keyboard navigation accessibility.
- Cleaned up and isolated component responsibilities for clarity and maintenance.
2023-11-05 15:55:38 -05:00
24 changed files with 470 additions and 168 deletions
+9
View File
@@ -169,6 +169,15 @@
"contributions": [ "contributions": [
"bug" "bug"
] ]
},
{
"login": "lsprr",
"name": "Luis Parra",
"avatar_url": "https://avatars.githubusercontent.com/u/16653744?v=4",
"profile": "https://luisparra.dev",
"contributions": [
"a11y"
]
} }
] ]
} }
+3
View File
@@ -47,3 +47,6 @@ MAILGUN_SENDER=postmaster@mail.yourdomain.com
# Base64 encoded PFX or p12 document signing certificate file ********************************************************************************************************************* # Base64 encoded PFX or p12 document signing certificate file *********************************************************************************************************************
PFX_BASE64= PFX_BASE64=
# Provide Pass pharse of above PFX or p12 document
PASS_PHRASE=
+3
View File
@@ -51,3 +51,6 @@ MAILGUN_SENDER=postmaster@mail.yourdomain.com
# Base64 encoded PFX or p12 document signing certificate file ********************************************************************************************************************* # Base64 encoded PFX or p12 document signing certificate file *********************************************************************************************************************
PFX_BASE64= PFX_BASE64=
# Provide Pass pharse of above PFX or p12 document
PASS_PHRASE=
+1
View File
@@ -124,6 +124,7 @@ We would like to thank all our contributors and users for their support and feed
<td align="center" valign="top" width="14.28%"><a href="https://github.com/andrew-opensignlabs"><img src="https://avatars.githubusercontent.com/u/148278535?v=4?s=100" width="100px;" alt="Andrew"/><br /><sub><b>Andrew</b></sub></a><br /><a href="#code-andrew-opensignlabs" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/andrew-opensignlabs"><img src="https://avatars.githubusercontent.com/u/148278535?v=4?s=100" width="100px;" alt="Andrew"/><br /><sub><b>Andrew</b></sub></a><br /><a href="#code-andrew-opensignlabs" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rishabjasrotia"><img src="https://avatars.githubusercontent.com/u/33950743?v=4?s=100" width="100px;" alt="Rishab"/><br /><sub><b>Rishab</b></sub></a><br /><a href="#code-rishabjasrotia" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/rishabjasrotia"><img src="https://avatars.githubusercontent.com/u/33950743?v=4?s=100" width="100px;" alt="Rishab"/><br /><sub><b>Rishab</b></sub></a><br /><a href="#code-rishabjasrotia" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://session.it"><img src="https://avatars.githubusercontent.com/u/327285?v=4?s=100" width="100px;" alt="Maurizio Pillitu"/><br /><sub><b>Maurizio Pillitu</b></sub></a><br /><a href="#bug-maoo" title="Bug reports">🐛</a></td> <td align="center" valign="top" width="14.28%"><a href="https://session.it"><img src="https://avatars.githubusercontent.com/u/327285?v=4?s=100" width="100px;" alt="Maurizio Pillitu"/><br /><sub><b>Maurizio Pillitu</b></sub></a><br /><a href="#bug-maoo" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://luisparra.dev"><img src="https://avatars.githubusercontent.com/u/16653744?v=4?s=100" width="100px;" alt="Luis Parra"/><br /><sub><b>Luis Parra</b></sub></a><br /><a href="#a11y-lsprr" title="Accessibility">️♿️</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
+2 -2
View File
@@ -40,8 +40,8 @@
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
"scripts": { "scripts": {
"start": "mf-cra start", "start": "serve -s build",
"start-prod": "serve -s build", "start-dev": "mf-cra start",
"build": "rm -rf public/mfbuild && npm run micro && CI=false && mf-cra build", "build": "rm -rf public/mfbuild && npm run micro && CI=false && mf-cra build",
"micro": "cd ../../microfrontends/SignDocuments && npm install && npm run build", "micro": "cd ../../microfrontends/SignDocuments && npm install && npm run build",
"test": "react-scripts test", "test": "react-scripts test",
+2
View File
@@ -14,6 +14,7 @@ import PageNotFound from "./routes/PageNotFound";
import ForgetPassword from "./routes/ForgetPassword"; import ForgetPassword from "./routes/ForgetPassword";
import ChangePassword from "./routes/ChangePassword"; import ChangePassword from "./routes/ChangePassword";
import ReportMicroapp from "./components/ReportMicroapp"; import ReportMicroapp from "./components/ReportMicroapp";
import LoadMf from "./routes/LoadMf";
function App() { function App() {
const [isloading, setIsLoading] = useState(true); const [isloading, setIsLoading] = useState(true);
@@ -59,6 +60,7 @@ function App() {
<Routes> <Routes>
<Route exact path="/" element={<Login />} /> <Route exact path="/" element={<Login />} />
<Route exact path="/signup" element={<Signup />} /> <Route exact path="/signup" element={<Signup />} />
<Route exact path="/loadmf/:remoteApp/*" element={<LoadMf />} />
<Route exact path="/forgetpassword" element={<ForgetPassword />} /> <Route exact path="/forgetpassword" element={<ForgetPassword />} />
{process.env.REACT_APP_ENABLE_SUBSCRIPTION && ( {process.env.REACT_APP_ENABLE_SUBSCRIPTION && (
<> <>
@@ -82,7 +82,7 @@ const FileUpload = (props) => {
progress: (progressValue, loaded, total, { type }) => { progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) { if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total); const percentCompleted = Math.round((loaded * 100) / total);
console.log("percentCompleted ", percentCompleted); // console.log("percentCompleted ", percentCompleted);
setpercentage(percentCompleted); setpercentage(percentCompleted);
} }
} }
@@ -0,0 +1,21 @@
import React from "react";
import { NavLink } from "react-router-dom";
const Menu = ({ item, isOpen, closeSidebar }) => {
return (
<li key={item.title} role="none">
<NavLink
to={`/${item.pageType}/${item.objectId}`}
className="mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4"
onClick={closeSidebar}
tabIndex={isOpen ? 0 : -1}
role="menuitem"
>
<i className={`${item.icon} text-[18px]`} aria-hidden="true"></i>
<span className="ml-3 lg:ml-4">{item.title}</span>
</NavLink>
</li>
)
}
export default Menu;
+34 -72
View File
@@ -1,115 +1,77 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { Link, NavLink } from "react-router-dom";
import Menu from './Menu';
import Submenu from "./SubMenu"; import Submenu from "./SubMenu";
import dp from "../../assets/images/dp.png"; import SocialMedia from './SocialMedia';
import Parse from "parse"; import Parse from "parse";
import dp from "../../assets/images/dp.png";
const Sidebar = ({ isOpen, closeSidebar }) => { const Sidebar = ({ isOpen, closeSidebar }) => {
const [menuList, setmenuList] = useState([]);
let username = localStorage.getItem("username"); let username = localStorage.getItem("username");
const image = localStorage.getItem("profileImg") || dp; const image = localStorage.getItem("profileImg") || dp;
const tenantname = localStorage.getItem("TenantName"); const tenantname = localStorage.getItem("TenantName");
const [menuList, setmenuList] = useState([]);
useEffect(() => { useEffect(() => {
if (localStorage.getItem("accesstoken")) { if (localStorage.getItem("accesstoken")) {
menuItem(); menuItem();
} }
}, []); }, []);
const menuItem = async () => { const menuItem = async () => {
const parseBaseUrl = localStorage.getItem("baseUrl"); const parseBaseUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId"); const parseAppId = localStorage.getItem("parseAppId");
try { try {
Parse.serverURL = parseBaseUrl; Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId); Parse.initialize(parseAppId);
var sideMenu = Parse.Object.extend("w_menu"); var sideMenu = Parse.Object.extend("w_menu");
var query = new Parse.Query(sideMenu); var query = new Parse.Query(sideMenu);
query.equalTo("objectId", localStorage.getItem("defaultmenuid")); query.equalTo("objectId", localStorage.getItem("defaultmenuid"));
const results = await query.first(); const results = await query.first();
const resultjson = results.toJSON(); const resultjson = results.toJSON();
// console.log("resultjson ", resultjson);
let result = resultjson; let result = resultjson;
setmenuList(result.menuItems); setmenuList(result.menuItems);
} catch (e) { } catch (e) {
console.error("Problem", e); console.error("Problem", e);
} }
}; };
return ( return (
<div <aside
className={`${ className={`${isOpen ? "block" : "hidden"
isOpen ? "block" : "hidden" } bg-white text-[#444] h-screen w-full md:w-[300px] overflow-y-auto transform transition-transform z-40 ${isOpen ? "translate-x-0" : "-translate-x-full"
} bg-white text-[#444] h-screen w-full md:w-[300px] overflow-y-auto transform transition-transform z-40 ${ }`}
isOpen ? "translate-x-0" : "-translate-x-full"
}`}
> >
<div className="flex px-2 py-3 gap-2 items-center shadow-md"> <div className="flex px-2 py-3 gap-2 items-center shadow-md">
<div className="w-[75px] h-[75px] rounded-full ring-[2px] ring-offset-2 ring-gray-400 overflow-hidden"> <div className="w-[75px] h-[75px] rounded-full ring-[2px] ring-offset-2 ring-gray-400 overflow-hidden">
<img className="w-full h-full object-contain" src={image} alt="img" /> <img className="w-full h-full object-cover" src={image} alt="Profile" />
</div> </div>
<div> <div>
<p className="text-[14px] font-bold">{username && username}</p> <p className="text-[14px] font-bold">{username}</p>
<p <p className={`text-[12px] ${tenantname ? "mt-2" : ""}`}>
className={`text-[12px] ${tenantname && tenantname ? "mt-2" : ""}`} {tenantname}
>
{tenantname && tenantname}
</p> </p>
</div> </div>
</div> </div>
<ul className="text-sm"> <nav aria-label="OpenSign Sidebar Navigation">
{menuList.length > 0 && ( <ul className="text-sm" role="menubar" aria-label="OpenSign Sidebar Navigation">
<> {menuList.map((item) => (
{menuList.map((item) => !item.children ? (
!item.children ? ( <Menu key={item.title} item={item} isOpen={isOpen} closeSidebar={closeSidebar} />
<li key={item.title} onClick={() => closeSidebar()}> ) : (
<Link <Submenu key={item.title} item={item} closeSidebar={closeSidebar} />
className={`mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4 hover:no-underline cursor-pointer`} )
to={`/${item.pageType}/${item.objectId}`} ))}
> </ul>
<i className={item.icon + " text-[18px]"}></i> </nav>
<span title={item.description} className="ml-3 lg:ml-4"> <footer className="mt-4 flex justify-center items-center text-[25px] text-black gap-3">
{item.title} <SocialMedia />
</span> </footer>
</Link> </aside>
</li>
) : (
<Submenu key={item.title} icon={item.icon} title={item.title}>
{item.children.map((item) => (
<li key={item.title} onClick={() => closeSidebar()} className="pl-6 md:pl-8 hover:bg-[#eef1f5] cursor-pointer">
<Link
className={`mx-auto flex items-center p-2 lg:p-3 hover:no-underline`}
to={`/${item.pageType}/${item.objectId}`}
>
<i className={item.icon + " text-[18px]"}></i>
<span title={item.description} className="ml-3 lg:ml-4">
{item.title}
</span>
</Link>
</li>
))}
</Submenu>
)
)}
</>
)}
</ul>
<div className="mt-4 flex justify-center items-center text-[25px] text-black gap-3">
<NavLink to="https://github.com/opensignlabs/opensign" target="_blank">
<i className="fa-brands fa-github"></i>
</NavLink>
<NavLink
to="https://www.linkedin.com/company/opensign%E2%84%A2/"
target="_blank"
>
<i className="fa-brands fa-linkedin"></i>
</NavLink>
<NavLink to="https://www.twitter.com/opensignlabs" target="_blank">
<i className="fa-brands fa-square-x-twitter"></i>
</NavLink>
<NavLink to="https://discord.com/invite/xe9TDuyAyj" target="_blank">
<i className="fa-brands fa-discord"></i>
</NavLink>
</div>
</div>
); );
}; };
@@ -0,0 +1,27 @@
import React from "react";
import { NavLink } from "react-router-dom";
const SocialMedia = () => {
return (
<React.Fragment>
<NavLink to="https://github.com/opensignlabs/opensign" target="_blank" rel="noopener noreferrer">
<i aria-hidden="true" className="fa-brands fa-github"></i>
<span className="fa-sr-only">OpenSign&apos;s Github</span>
</NavLink>
<NavLink to="https://www.linkedin.com/company/opensign%E2%84%A2/" target="_blank" rel="noopener noreferrer">
<i aria-hidden="true" className="fa-brands fa-linkedin"></i>
<span className="fa-sr-only">OpenSign&apos;s LinkedIn</span>
</NavLink>
<NavLink to="https://www.twitter.com/opensignlabs" target="_blank" rel="noopener noreferrer">
<i aria-hidden="true" className="fa-brands fa-square-x-twitter"></i>
<span className="fa-sr-only">OpenSign&apos;s Twitter</span>
</NavLink>
<NavLink to="https://discord.com/invite/xe9TDuyAyj" target="_blank" rel="noopener noreferrer">
<i aria-hidden="true" className="fa-brands fa-discord"></i>
<span className="fa-sr-only">OpenSign&apos;s Discord</span>
</NavLink>
</React.Fragment>
)
}
export default SocialMedia;
+33 -11
View File
@@ -1,30 +1,52 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { NavLink } from "react-router-dom";
const Submenu = ({ icon, title, children }) => { const Submenu = ({ item, closeSidebar }) => {
const [isOpen, setIsOpen] = useState(false); const [submenuOpen, setSubmenuOpen] = useState(false);
const { title, icon, children } = item;
const toggleSubmenu = () => { const toggleSubmenu = () => {
setIsOpen(!isOpen); setSubmenuOpen(!submenuOpen);
}; };
return ( return (
<div> <li role="none">
<button <button
onClick={toggleSubmenu} onClick={toggleSubmenu}
className="w-full mx-auto flex items-center text-left text-[#444] p-3 lg:p-4 hover:bg-[#eef1f5] focus:outline-none" className="w-full flex items-center text-left text-[#444] p-3 lg:p-4 hover:bg-[#eef1f5] focus:ring-2 focus:ring-blue-600"
aria-expanded={submenuOpen}
aria-haspopup="true"
aria-controls={`submenu-${title}`}
> >
<i className={icon + " text-[18px]"}></i> <i className={`${icon} text-[18px]`}></i>
<div className="flex justify-between items-center w-full"> <div className="flex justify-between items-center w-full">
<span className="ml-3 lg:ml-4">{title}</span> <span className="ml-3 lg:ml-4">{title}</span>
<i <i
className={ className={`${submenuOpen ? "fa-solid fa-angle-down" : "fa-solid fa-angle-right"
isOpen ? "fa-solid fa-angle-down" : "fa-solid fa-angle-right" }`}
} aria-hidden="true"
></i> ></i>
</div> </div>
</button> </button>
{isOpen && <ul>{children}</ul>} {submenuOpen && (
</div> <ul id={`submenu-${title}`} role="menu" aria-label={`${title} submenu`}>
{children.map((childItem) => (
<li key={childItem.title} role="none">
<NavLink
to={`/${childItem.pageType}/${childItem.objectId}`}
className="block pl-6 md:pl-8 py-2 text-sm text-gray-700 hover:bg-blue-500 hover:text-white"
onClick={closeSidebar}
role="menuitem"
tabIndex={submenuOpen ? 0 : -1}
>
<i className={`${childItem.icon} text-[18px]`} aria-hidden="true"></i>
<span className="ml-3 lg:ml-4">{childItem.title}</span>
</NavLink>
</li>
))}
</ul>
)}
</li>
); );
}; };
@@ -1,9 +1,37 @@
import React from "react"; import React, { useState, useEffect, useMemo } from "react";
import pad from "../assets/images/pad.svg"; import pad from "../assets/images/pad.svg";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
const ReportTable = ({ ReportName, List, actions }) => { const ReportTable = ({
ReportName,
List,
actions,
setIsNextRecord,
isMoreDocs
}) => {
const navigate = useNavigate(); const navigate = useNavigate();
const [currentPage, setCurrentPage] = useState(1);
const docPerPage = 10;
// const pageNumbers = [];
// For loop is used to calculate page numbers visible below table
// Initialize pageNumbers using useMemo to avoid unnecessary re-creation
const pageNumbers = useMemo(() => {
const calculatedPageNumbers = [];
for (let i = 1; i <= Math.ceil(List.length / docPerPage); i++) {
calculatedPageNumbers.push(i);
}
return calculatedPageNumbers;
}, [List]);
// below useEffect reset currenpage to 1 if user change route
useEffect(() => {
return () => setCurrentPage(1);
}, []);
// `formatRow` is used to show data in poper manner like
// if data is of array type then it will join array items with ","
// if data is of object type then it Name values will be show in row
// if no data available it will show hyphen "-"
const formatRow = (row) => { const formatRow = (row) => {
if (Array.isArray(row)) { if (Array.isArray(row)) {
let updateArr = row.map((x) => x.Name); let updateArr = row.map((x) => x.Name);
@@ -14,24 +42,17 @@ const ReportTable = ({ ReportName, List, actions }) => {
return "-"; return "-";
} }
}; };
// below useEffect is used to render next record if IsMoreDoc is true
// second last value of pageNumber array is same as currentPage
useEffect(() => {
if (isMoreDocs && pageNumbers[pageNumbers.length - 1] === currentPage) {
setIsNextRecord(true);
}
}, [isMoreDocs, pageNumbers, currentPage, setIsNextRecord]);
// `handlemicroapp` is used to open microapp
const handlemicroapp = (item, url) => { const handlemicroapp = (item, url) => {
localStorage.removeItem("rowlevel"); localStorage.removeItem("rowlevel");
// const params = new URLSearchParams(url);
// let arr = [];
// for (let [key, value] of params.entries()) {
// if (key === "remoteUrl") {
// arr.push(
// `${key}=${window.btoa(
// window.location.origin + "/mfbuild/remoteEntry.js"
// )}`
// );
// } else {
// arr.push(`${key}=${value}`);
// }
// }
// const remoteUrl = arr.join("&");
// console.log("arr", remoteUrl);
navigate("/rpmf/" + url); navigate("/rpmf/" + url);
localStorage.setItem("rowlevel", JSON.stringify(item)); localStorage.setItem("rowlevel", JSON.stringify(item));
// localStorage.setItem("rowlevelMicro"); // localStorage.setItem("rowlevelMicro");
@@ -39,6 +60,17 @@ const ReportTable = ({ ReportName, List, actions }) => {
const handlebtn = () => { const handlebtn = () => {
console.log("clicked"); console.log("clicked");
}; };
// Get current list
const indexOfLastDoc = currentPage * docPerPage;
const indexOfFirstDoc = indexOfLastDoc - docPerPage;
// `currentLists` is total record render on current page
const currentLists = List.slice(indexOfFirstDoc, indexOfLastDoc);
// Change page
const paginateFront = () => setCurrentPage(currentPage + 1);
const paginateBack = () => setCurrentPage(currentPage - 1);
return ( return (
<div className="p-2 overflow-x-scroll w-full bg-white rounded-md"> <div className="p-2 overflow-x-scroll w-full bg-white rounded-md">
<h2 className="text-[23px] font-light my-2">{ReportName}</h2> <h2 className="text-[23px] font-light my-2">{ReportName}</h2>
@@ -60,8 +92,8 @@ const ReportTable = ({ ReportName, List, actions }) => {
<tbody className="text-[12px]"> <tbody className="text-[12px]">
{List?.length > 0 ? ( {List?.length > 0 ? (
<> <>
{List.map((item, index) => ( {currentLists.map((item, index) => (
<tr className="border-t-[1px]" key={index}> <tr className="border-y-[1px]" key={index}>
<td className="px-4 py-2">{index + 1}</td> <td className="px-4 py-2">{index + 1}</td>
<td className="px-4 py-2 font-semibold">{item?.Name} </td> <td className="px-4 py-2 font-semibold">{item?.Name} </td>
<td className="px-4 py-2">{item?.Note || "-"}</td> <td className="px-4 py-2">{item?.Note || "-"}</td>
@@ -121,8 +153,46 @@ const ReportTable = ({ ReportName, List, actions }) => {
)} )}
</tbody> </tbody>
</table> </table>
<div className="flex flex-wrap items-center gap-2 p-2 ">
{List.length > 10 && (
<>
{currentPage > 1 && (
<button
onClick={() => paginateBack()}
className="bg-blue-400 text-white rounded px-3 py-2 focus:outline-none"
>
Prev
</button>
)}
</>
)}
{pageNumbers.map((x) => (
<button
key={x}
onClick={() => setCurrentPage(x)}
className=" bg-sky-400 text-white rounded px-3 py-2 focus:outline-none"
>
{x}
</button>
))}
{isMoreDocs && (
<button className="text-black rounded px-1 py-2">...</button>
)}
{List.length > 10 && (
<>
{pageNumbers.includes(currentPage + 1) && (
<button
onClick={() => paginateFront()}
className="bg-blue-400 text-white rounded px-3 py-2 focus:outline-none"
>
Next
</button>
)}
</>
)}
</div>
{List?.length <= 0 && ( {List?.length <= 0 && (
<div className="flex flex-col items-center justify-center w-full bg-white roundedm py-4"> <div className="flex flex-col items-center justify-center w-full bg-white rounded py-4">
<div className="w-[60px] h-[60px] overflow-hidden"> <div className="w-[60px] h-[60px] overflow-hidden">
<img className="w-full h-full object-contain" src={pad} alt="img" /> <img className="w-full h-full object-contain" src={pad} alt="img" />
</div> </div>
+84
View File
@@ -0,0 +1,84 @@
import { Suspense } from "react";
import { useParams } from "react-router-dom";
import useFederatedComponent from "mf-cra";
import React from "react";
import Title from "../components/Title";
function RemoteApp({ app }) {
// console.log("app ", app);
const { Component: RemoteComponent } = useFederatedComponent(app);
// console.log("RemoteComponent ", RemoteComponent);
return (
<>
<Title title={app.remoteName} />
<Suspense
fallback={
<div style={{ height: "300px" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
}
>
<div
style={{
boxShadow: "0 1px 3px 0 rgba(0, 0, 0, 0.33)",
backgroundColor: "#ffffff",
width: "100%",
overflow: "hidden",
borderRadius: 3,
minHeight: "70vh"
}}
>
{RemoteComponent && <RemoteComponent />}
</div>
</Suspense>
</>
);
}
function checkObjectProperties(obj) {
// Check if all three properties exist in the object
if (
obj &&
obj.remoteName !== undefined &&
obj.remoteUrl !== undefined &&
obj.moduleToLoad !== undefined
) {
return true; // All properties are present
} else {
return false; // At least one property is missing
}
}
export default function RemoteAppContainer() {
const { remoteApp } = useParams();
const obj = {
remoteUrl: window.location.origin + "/mfbuild/remoteEntry.js",
moduleToLoad: "./AppRoutes",
remoteName: remoteApp
};
// console.log("obj ", obj);
const isMf = checkObjectProperties(obj);
if (isMf) {
const appToLoad = obj;
return <RemoteApp app={appToLoad} />;
} else {
return (
<div className="flex items-center justify-center h-screen w-full bg-white rounded">
<div className="text-center">
<h1 className="text-[60px] lg:text-[120px] font-semibold text-black">
404
</h1>
<p className="text-[30px] lg:text-[50px] text-black">
Page Not Found
</p>
</div>
</div>
);
}
}
+46 -38
View File
@@ -744,7 +744,7 @@ function Login(props) {
}; };
const GetLoginData = async () => { const GetLoginData = async () => {
setState({...state, loading: true}) setState({ ...state, loading: true })
try { try {
Parse.serverURL = localStorage.getItem("baseUrl"); Parse.serverURL = localStorage.getItem("baseUrl");
Parse.initialize(localStorage.getItem("parseAppId")); Parse.initialize(localStorage.getItem("parseAppId"));
@@ -925,6 +925,7 @@ function Login(props) {
<Title title={"Login Page"} /> <Title title={"Login Page"} />
{state.loading && ( {state.loading && (
<div <div
aria-live="assertive"
style={{ style={{
position: "fixed", position: "fixed",
width: "100vw", width: "100vw",
@@ -936,63 +937,66 @@ function Login(props) {
}} }}
> >
<div <div
role="status"
style={{ style={{
position: "fixed", position: "fixed",
fontSize: "50px", fontSize: "50px",
color: "#3ac9d6", color: "#3ac9d6",
top: "50%", top: "50%",
left: "45%" left: "50%",
transform: 'translate(-50%, -50%)'
}} }}
className="loader-37" className="loader-37"
></div> >
<span className="sr-only">Loading...</span>
</div>
</div> </div>
)} )}
{props.isloginVisible && props.isloginVisible ? ( {props.isloginVisible && props.isloginVisible ? (
<div> <div aria-labelledby="loginHeading" role="region">
<div className="md:m-10 lg:m-16 md:p-4 lg:p-10 p-5 bg-[#ffffff] md:border-[1px] md:border-gray-400 "> <div className="md:m-10 lg:m-16 md:p-4 lg:p-10 p-5 bg-[#ffffff] md:border-[1px] md:border-gray-400 ">
<div className="w-[250px] h-[66px] inline-block"> <div className="w-[250px] h-[66px] inline-block">
{state.hideNav ? ( {state.hideNav ? (
<img src={image} width="100%" alt="" /> <img src={image} width="100%" alt="The image displays the OpenSign logo with a stylized blue square with an open corner, accompanied by the tagline Seal the Deal, Openly." />
) : ( ) : (
<img src={image} width="100%" alt="" /> <img src={image} width="100%" alt="The image displays the OpenSign logo with a stylized blue square with an open corner, accompanied by the tagline Seal the Deal, Openly." />
)} )}
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2"> <div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
<div className=""> <div>
<div> <div>
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit} aria-label="Login Form">
<h2 className="text-[30px] mt-6">Welcome Back !</h2> <h1 className="text-[30px] mt-6">Welcome Back!</h1>
<span className="text-[12px] text-[#878787]"> <fieldset className="outline outline-1 outline-slate-300/50 my-4">
Login to your account <legend className="text-xs font-bold px-6 mt-4">Login to your account</legend>
</span>
<div className="shadow-md rounded my-4">
<div className="px-6 py-4"> <div className="px-6 py-4">
<label className="block text-xs">Username</label> <label className="block text-xs" htmlFor="email">Username</label>
<input <input
id="email"
type="text" type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs" className="px-3 py-2 w-full border-[1px] border-gray-300 rounded text-xs"
name="email" name="email"
value={state.email} value={state.email}
onChange={handleChange} onChange={handleChange}
required required
/> />
<hr className="my-2 border-none" /> <hr className="my-2 border-none" />
<label className="block text-xs">Password</label> <label className="block text-xs" htmlFor="password">Password</label>
<div className="relative"> <div className="relative">
<input <input
id="password"
type={state.passwordVisible ? "text" : "password"} type={state.passwordVisible ? "text" : "password"}
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs" className="px-3 py-2 w-full border-[1px] border-gray-300 rounded text-xs"
name="password" name="password"
value={state.password} value={state.password}
onChange={handleChange} onChange={handleChange}
required required
/> />
<span <span
className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer ${ className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer ${state.passwordVisible
state.passwordVisible ? "text-[#007bff]"
? "text-[#007bff]" : "text-black"
: "text-black" }`}
}`}
onClick={togglePasswordVisibility} onClick={togglePasswordVisibility}
> >
{state.passwordVisible ? ( {state.passwordVisible ? (
@@ -1003,32 +1007,32 @@ function Login(props) {
</span> </span>
</div> </div>
</div> </div>
</div> </fieldset>
<div className="flex flex-row justify-between items-center text-xs px-4 py-2"> <div className="flex flex-row justify-between items-center px-4 py-2">
<div> <div>
<label className="form-check-label"> <label className="form-check-label inline-block cursor-pointer" htmlFor="rememberpassword">
<input <input
type="checkbox" type="checkbox"
className="form-check-input" className="form-check-input mr-2"
value="" value=""
id="rememberpassword"
/> />
<span className="text-center pl-1"> <span className="text-[13px] pl-1">Remember Password</span>
Remember Password
</span>
</label> </label>
</div> </div>
<div> <div>
<span className="text-[13px]"> <NavLink
<NavLink to={`/forgetpassword`}> to="/forgetpassword"
Forgot Password ? className="text-[13px] underline focus:outline-none focus:ring-2 focus:ring-blue-600"
</NavLink> >
</span> Forgot Password?
</NavLink>
</div> </div>
</div> </div>
<div className="flex flex-col md:flex-row justify-between items-stretch gap-8 text-center text-xs font-bold mt-2"> <div className="flex flex-col md:flex-row justify-between items-stretch gap-8 text-center text-xs font-bold mt-2">
<button <button
type="submit" type="submit"
className="rounded-sm bg-[#3ac9d6] text-white w-full py-3 shadow uppercase" className="rounded-sm bg-[#3ac9d6] text-white w-full py-3 shadow outline-none uppercase focus:ring-2 focus:ring-blue-600"
disabled={state.loading} disabled={state.loading}
> >
{state.loading ? "Loading..." : "Login"} {state.loading ? "Loading..." : "Login"}
@@ -1093,7 +1097,7 @@ function Login(props) {
}} }}
> >
{props.appInfo.googleClietId && {props.appInfo.googleClietId &&
props.appInfo.googleClietId !== "" ? ( props.appInfo.googleClietId !== "" ? (
<GoogleSignInBtn <GoogleSignInBtn
GoogleCred={props.appInfo.googleClietId} GoogleCred={props.appInfo.googleClietId}
thirdpartyLoginfn={thirdpartyLoginfn} thirdpartyLoginfn={thirdpartyLoginfn}
@@ -1107,14 +1111,18 @@ function Login(props) {
{!state.hideNav && ( {!state.hideNav && (
<div className="self-center"> <div className="self-center">
<div className="mx-auto md:w-[300px] lg:w-[500px]"> <div className="mx-auto md:w-[300px] lg:w-[500px]">
<img src={login_img} alt="bisec" width="100%" /> <img src={login_img} alt="The image illustrates a person from behind, seated at a desk with a four-monitor computer setup, in an environment with a light blue and white color scheme, featuring a potted plant to the right." width="100%" />
</div> </div>
</div> </div>
)} )}
</div> </div>
</div> </div>
<div id="snackbar" style={{ backgroundColor: state.toastColor }}> <div
id="snackbar"
role="alert"
style={{ backgroundColor: state.toastColor }}
>
{state.toastDescription} {state.toastDescription}
</div> </div>
</div> </div>
@@ -64,7 +64,7 @@ const PlanSubscriptions = () => {
style={{ style={{
backgroundColor: "white", backgroundColor: "white",
overflowY: "auto", overflowY: "auto",
maxHeight: "600px", maxHeight: "100%",
"--theme-color": "#7952b3", "--theme-color": "#7952b3",
"--plan-width": 30 "--plan-width": 30
}} }}
+57 -8
View File
@@ -12,14 +12,37 @@ const Report = () => {
const [isLoader, setIsLoader] = useState(true); const [isLoader, setIsLoader] = useState(true);
const [reportName, setReportName] = useState(""); const [reportName, setReportName] = useState("");
const [actions, setActions] = useState([]); const [actions, setActions] = useState([]);
const [isNextRecord, setIsNextRecord] = useState(false);
const [isMoreDocs, setIsMoreDocs] = useState(true);
const abortController = new AbortController();
// below useEffect is call when id param change
useEffect(() => { useEffect(() => {
setReportName(""); setReportName("");
setList([]);
getReportData(); getReportData();
// Function returned from useEffect is called on unmount
return () => {
setIsLoader(true);
setList([]);
setIsNextRecord(false);
// Here it'll abort the fetch
abortController.abort();
};
// eslint-disable-next-line // eslint-disable-next-line
}, [id]); }, [id]);
const getReportData = async () => { // below useEffect call when isNextRecord state is true and fetch next record
setIsLoader(true); useEffect(() => {
if (isNextRecord) {
getReportData(List.length, 200);
}
// eslint-disable-next-line
}, [isNextRecord]);
const getReportData = async (skipUserRecord = 0, limit = 200) => {
// setIsLoader(true);
const json = reportJson(id); const json = reportJson(id);
if (json) { if (json) {
setActions(json.actions); setActions(json.actions);
@@ -39,8 +62,11 @@ const Report = () => {
"X-Parse-Session-Token": localStorage.getItem("accesstoken") "X-Parse-Session-Token": localStorage.getItem("accesstoken")
}; };
try { try {
const url = `${serverURL}?where=${strParams}&keys=${strKeys}&order=${orderBy}&include=AuditTrail.UserPtr`; const url = `${serverURL}?where=${strParams}&keys=${strKeys}&order=${orderBy}&skip=${skipUserRecord}&limit=${limit}&include=AuditTrail.UserPtr`;
const res = await axios.get(url, { headers: headers }); const res = await axios.get(url, {
headers: headers,
signal: abortController.signal // is used to cancel fetch query
});
if (id === "4Hhwbp482K") { if (id === "4Hhwbp482K") {
const listData = res.data?.results.filter( const listData = res.data?.results.filter(
(x) => x.Signers.length > 0 (x) => x.Signers.length > 0
@@ -66,14 +92,35 @@ const Report = () => {
} }
} }
} }
setList(arr); if (arr.length === 10) {
setIsMoreDocs(true);
} else {
setIsMoreDocs(false);
}
setList((prevRecord) =>
prevRecord.length > 0 ? [...prevRecord, ...arr] : arr
);
} else { } else {
setList(res.data?.results); if (res.data.results.length === 10) {
setIsMoreDocs(true);
console.log("here");
} else {
setIsMoreDocs(false);
}
setIsNextRecord(false);
setList((prevRecord) =>
prevRecord.length > 0
? [...prevRecord, ...res.data.results]
: res.data.results
);
} }
setIsLoader(false); setIsLoader(false);
} catch (err) { } catch (err) {
console.log("err ", err); const isCancel = axios.isCancel(err);
setIsLoader(false); if (!isCancel) {
console.log("err ", err);
setIsLoader(false);
}
} }
} else { } else {
setIsLoader(false); setIsLoader(false);
@@ -106,6 +153,8 @@ const Report = () => {
ReportName={reportName} ReportName={reportName}
List={List} List={List}
actions={actions} actions={actions}
setIsNextRecord={setIsNextRecord}
isMoreDocs={isMoreDocs}
/> />
) : ( ) : (
<div className="flex items-center justify-center h-screen w-full bg-white rounded"> <div className="flex items-center justify-center h-screen w-full bg-white rounded">
+1 -1
View File
@@ -251,7 +251,7 @@ const Signup = (props) => {
element.pageType element.pageType
); );
setState({ loading: false }); setState({ loading: false });
navigate("/login"); navigate("/");
} else { } else {
extendedInfo.forEach((x) => { extendedInfo.forEach((x) => {
if (x.TenantId) { if (x.TenantId) {
@@ -1 +1 @@
const signer=require("node-signpdf").default;class SignPDF{constructor(e,t){this.pdfDoc=e,this.certificate=t}async signPDF(){return signer.sign(this.pdfDoc,this.certificate,{passphrase:"emudhra"})}static unit8ToBuffer(e){var t=Buffer.alloc(e.byteLength),r=new Uint8Array(e);for(let e=0;e<t.length;++e)t[e]=r[e];return t}}module.exports=SignPDF; const signer=require("node-signpdf").default;class SignPDF{constructor(e,t){this.pdfDoc=e,this.certificate=t}async signPDF(){return signer.sign(this.pdfDoc,this.certificate, process.env.PASS_PHRASE ? {passphrase : process.env.PASS_PHRASE}: null)}static unit8ToBuffer(e){var t=Buffer.alloc(e.byteLength),r=new Uint8Array(e);for(let e=0;e<t.length;++e)t[e]=r[e];return t}}module.exports=SignPDF;
@@ -0,0 +1,16 @@
import React from "react";
const PageNotFound = () => {
return (
<div className="flex items-center justify-center h-screen w-full bg-white rounded">
<div className="text-center">
<h1 className="text-[60px] lg:text-[120px] font-semibold text-black">
404
</h1>
<p className="text-[30px] lg:text-[50px] text-black">Page Not Found</p>
</div>
</div>
);
};
export default PageNotFound;
@@ -109,8 +109,17 @@ function EmailComponent({
const handleToPrint = async (event) => { const handleToPrint = async (event) => {
event.preventDefault(); event.preventDefault();
const base64 = await getBase64FromUrl(pdfUrl); const pdf = await getBase64FromUrl(pdfUrl);
printModule({ printable: base64, type: "pdf", base64: true }); const isAndroidDevice = navigator.userAgent.match(/Android/i);
const isAppleDevice = (/iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) && !window.MSStream
if (isAndroidDevice || isAppleDevice) {
const byteArray = Uint8Array.from(atob(pdf).split('').map(char => char.charCodeAt(0)));
const blob = new Blob([byteArray], { type: 'application/pdf' });
const blobUrl = URL.createObjectURL(blob);
window.open(blobUrl, '_blank');
} else {
printModule({ printable: pdf, type: "pdf", base64: true });
}
}; };
@@ -60,8 +60,16 @@ function Header({
event.preventDefault(); event.preventDefault();
const pdf = await getBase64FromUrl(pdfUrl); const pdf = await getBase64FromUrl(pdfUrl);
const isAndroidDevice = navigator.userAgent.match(/Android/i);
printModule({ printable: pdf, type: "pdf", base64: true }); const isAppleDevice = (/iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) && !window.MSStream
if (isAndroidDevice || isAppleDevice) {
const byteArray = Uint8Array.from(atob(pdf).split('').map(char => char.charCodeAt(0)));
const blob = new Blob([byteArray], { type: 'application/pdf' });
const blobUrl = URL.createObjectURL(blob);
window.open(blobUrl, '_blank');
} else {
printModule({ printable: pdf, type: "pdf", base64: true });
}
}; };
//handle download signed pdf //handle download signed pdf
@@ -116,7 +116,8 @@ function Login() {
localStorage.setItem("accesstoken", _user.sessionToken); localStorage.setItem("accesstoken", _user.sessionToken);
setLoading(false); setLoading(false);
//navigate user to on signature page //navigate user to on signature page
navigate(`/recipientSignPdf/${id}/${contactBookId}`); // navigate(`/recipientSignPdf/${id}/${contactBookId}`);
navigate(`/loadmf/signmicroapp/recipientSignPdf/${id}/${contactBookId}`);
} }
} catch (error) {} } catch (error) {}
} else { } else {
@@ -166,7 +166,7 @@ function PlaceHolderSign() {
getDocumentDetails(); getDocumentDetails();
} }
}, []); }, []);
//function for get document details //function for get document details
const getDocumentDetails = async () => { const getDocumentDetails = async () => {
await axios await axios
@@ -275,7 +275,7 @@ function PlaceHolderSign() {
getSignerPos(item, monitor); getSignerPos(item, monitor);
} }
}; };
const getSignerPos = (item, monitor) => { const getSignerPos = (item, monitor) => {
const key = Math.floor(1000 + Math.random() * 9000); const key = Math.floor(1000 + Math.random() * 9000);
let filterSignerPos = signerPos.filter( let filterSignerPos = signerPos.filter(
@@ -396,7 +396,7 @@ function PlaceHolderSign() {
blockColor: color[isSelectListId], blockColor: color[isSelectListId],
placeHolder: xyPosArr placeHolder: xyPosArr
}; };
setSignerPos((prev) => [...prev, placeHolderPos]); setSignerPos((prev) => [...prev, placeHolderPos]);
} }
}; };
@@ -626,7 +626,7 @@ function PlaceHolderSign() {
setSignBtnPosition([]); setSignBtnPosition([]);
setPageNumber((prevPageNumber) => prevPageNumber + offset); setPageNumber((prevPageNumber) => prevPageNumber + offset);
} }
//function for capture position on hover signature button //function for capture position on hover signature button
const handleDivClick = (e) => { const handleDivClick = (e) => {
const divRect = e.currentTarget.getBoundingClientRect(); const divRect = e.currentTarget.getBoundingClientRect();
@@ -679,7 +679,7 @@ function PlaceHolderSign() {
year: "numeric" year: "numeric"
}); });
let sender = signersdata.ExtUserPtr.Email; let sender = signersdata.ExtUserPtr.Email;
const signerMail = signersdata.Signers; const signerMail = signersdata.Signers;
for (let i = 0; i < signerMail.length; i++) { for (let i = 0; i < signerMail.length; i++) {
try { try {
@@ -698,7 +698,8 @@ function PlaceHolderSign() {
"parseAppId" "parseAppId"
)}&${localStorage.getItem("_appName")}`; )}&${localStorage.getItem("_appName")}`;
let signPdf = `https://qik-ai-org.github.io/Sign-MicroappV2/#/login/${signersdata.objectId}/${signerMail[i].Email}/${objectId}/${serverParams}`; const hostUrl = window.location.origin + "/loadmf/signmicroapp";
let signPdf = `${hostUrl}/login/${signersdata.objectId}/${signerMail[i].Email}/${objectId}/${serverParams}`;
const themeBGcolor = themeColor(); const themeBGcolor = themeColor();
let params = { let params = {
+7 -1
View File
@@ -8,6 +8,7 @@ import Login from "./Component/login";
import DraftDocument from "./Component/DraftDocument"; import DraftDocument from "./Component/DraftDocument";
import PdfRequestFiles from "./Component/PdfRequestFiles"; import PdfRequestFiles from "./Component/PdfRequestFiles";
import LegaDrive from "./Component/LegaDrive/LegaDrive"; import LegaDrive from "./Component/LegaDrive/LegaDrive";
import PageNotFound from "./Component/PageNotFound";
// `AppRoutes` is used to define route path of app and // `AppRoutes` is used to define route path of app and
// it expose to host app, check moduleFederation.config.js for more // it expose to host app, check moduleFederation.config.js for more
@@ -33,7 +34,10 @@ function AppRoutes() {
{/*Add default signature of user route */} {/*Add default signature of user route */}
<Route path="/managesign" element={<ManageSign />} /> <Route path="/managesign" element={<ManageSign />} />
{/* login page route */} {/* login page route */}
<Route path="/login/:id/:userMail/:contactBookId/:serverUrl" element={<Login />} /> <Route
path="/login/:id/:userMail/:contactBookId/:serverUrl"
element={<Login />}
/>
{/* draft document route to handle and navigate route page accordiing to document status */} {/* draft document route to handle and navigate route page accordiing to document status */}
<Route path="/draftDocument" element={<DraftDocument />} /> <Route path="/draftDocument" element={<DraftDocument />} />
{/* for user signature (need your sign route) with row level data */} {/* for user signature (need your sign route) with row level data */}
@@ -42,6 +46,8 @@ function AppRoutes() {
<Route path="/pdfRequestFiles/:docId" element={<PdfRequestFiles />} /> <Route path="/pdfRequestFiles/:docId" element={<PdfRequestFiles />} />
{/* lega drive route */} {/* lega drive route */}
<Route path="/legadrive" element={<LegaDrive />} /> <Route path="/legadrive" element={<LegaDrive />} />
{/* Page Not Found */}
<Route path="*" element={<PageNotFound />} />
</Routes> </Routes>
</div> </div>
); );