Compare commits

...
24 changed files with 126 additions and 66 deletions
@@ -12,12 +12,9 @@ function BorderResize(props) {
return (
<div
style={{
width: getHeight() || "14px",
height: getHeight() || "14px"
}}
style={{ width: getHeight() || "14px", height: getHeight() || "14px" }}
className={`${props.right ? `-right-[12px]` : "-right-[2px]"} ${
props.top ? `-bottom-[12px]` : "-bottom-[2px] "
props.top ? `-bottom-[12px]` : "-bottom-[2px]"
} absolute inline-block hover:cursor-sw-resize border-r-[3px] border-b-[3px] border-[#188ae2]`}
></div>
);
@@ -1,6 +1,7 @@
import { useState, useEffect, useRef } from "react";
const Cell = ({
isEnabled,
count,
h,
value,
@@ -15,10 +16,11 @@ const Cell = ({
hint
}) => (
<div
className="flex items-center justify-center border border-gray-800 bg-white"
className={`${isEnabled ? "bg-white border-gray-400" : "select-none-cls pointer-events-none border-gray-500"} flex items-center justify-center border-[1px]`}
style={{ flex: `0 0 ${100 / count}%`, height: h }}
>
<input
disabled={!isEnabled}
maxLength={1}
value={value}
readOnly={!editable}
@@ -27,7 +29,7 @@ const Cell = ({
onKeyDown={editable ? (e) => onKeyDown && onKeyDown(e, index) : undefined}
// trigger validation when leaving a cell
onBlur={editable ? (e) => onBlur && onBlur(e, index) : undefined}
className="w-full text-center focus:outline-none bg-transparent placeholder-gray-300"
className={`${isEnabled ? "placeholder-gray-300" : "placeholder-gray-500"} w-full text-center focus:outline-none bg-transparent`}
placeholder={hint}
style={{ fontFamily: "Arial, sans-serif", fontSize, color: fontColor }}
/>
@@ -35,6 +37,7 @@ const Cell = ({
);
export default function CellsWidget({
isEnabled,
count = 8,
height = 40,
value = "",
@@ -120,6 +123,7 @@ export default function CellsWidget({
{cells.map((val, i) => (
<Cell
key={i}
isEnabled={isEnabled}
count={cellCount}
h={height}
value={val}
@@ -740,7 +740,7 @@ function Placeholder(props) {
return "not-allowed";
}
} else {
return "all-scroll";
return "move";
}
};
@@ -946,23 +946,24 @@ function Placeholder(props) {
props.pos.key === props?.currWidgetsDetails?.key && <BorderResize />
)}
{/* 1- Show a ouline if props.pos.key === props?.currWidgetsDetails?.key, indicating the current user's selected widget.
2- If props.isShowBorder is true, display ouline for all widgets.
3- Use the combination of props?.isAlllowModify and !props?.assignedWidgetId.includes(props.pos.key) to determine when to show ouline:
3.1- When isAlllowModify is true, show ouline.
3.2- Do not display ouline for widgets already assigned (props.assignedWidgetId.includes(props.pos.key) is true).
{/* 1- Show a border if props.pos.key === props?.currWidgetsDetails?.key, indicating the current user's selected widget.
2- If props.isShowBorder is true, display border for all widgets.
3- Use the combination of props?.isAlllowModify and !props?.assignedWidgetId.includes(props.pos.key) to determine when to show border:
3.1- When isAlllowModify is true, show border.
3.2- Do not display border for widgets already assigned (props.assignedWidgetId.includes(props.pos.key) is true).
*/}
{props.pos.key === props?.currWidgetsDetails?.key &&
(props.isShowBorder ||
(props?.isAlllowModify &&
!props?.assignedWidgetId.includes(props.pos.key))) && (
<div
style={{ borderColor: themeColor }}
className="w-[calc(100%+21px)] h-[calc(100%+21px)] cursor-move absolute inline-block border-[1px] border-dashed"
></div>
)}
<div
className={`${
props.pos.key === props?.currWidgetsDetails?.key &&
(props.isShowBorder ||
(props?.isAlllowModify &&
!props?.assignedWidgetId.includes(props.pos.key)))
? "outline-[0.3px] outline-dashed outline-offset-[10px]"
: ""
} flex items-stretch justify-center`}
className="flex items-stretch justify-center"
style={{
outlineColor: themeColor,
left: xPos(props.pos, props.isSignYourself),
top: yPos(props.pos, props.isSignYourself),
width: "100%",
@@ -226,6 +226,7 @@ function PlaceholderType(props) {
props.isPlaceholder || props.isSignYourself || props.isSelfSign;
return (
<CellsWidget
isEnabled={iswidgetEnable}
count={count}
height={height}
value={cells.join("")}
@@ -123,6 +123,23 @@ const WidgetNameModal = (props) => {
}
};
const handleChangeValidateInput = (e) => {
if (e) {
if (e.target.value === "ssn") {
setFormdata({
...formdata,
[e.target.name]: e.target.value,
hint: "xxx-xx-xxxx",
cellCount: 11
});
} else {
setFormdata({ ...formdata, [e.target.name]: e.target.value });
}
} else {
setFormdata({ ...formdata, textvalidate: "" });
}
};
const handledefaultChange = (e) => {
if (formdata.textvalidate) {
const regexObject = RegexParser(handleValidation(formdata.textvalidate));
@@ -148,7 +165,7 @@ const WidgetNameModal = (props) => {
//allow space in text regex
return "/^[a-zA-Z ]+$/";
case "ssn":
return "^\\d{3}-\\d{2}-\\d{4}$";
return "/^(?!000|666|9\\d{2})\\d{3}-(?!00)\\d{2}-(?!0000)\\d{4}$/";
default:
return type;
}
@@ -694,17 +694,20 @@ function WidgetsValueModal(props) {
}
}
if (isTab === "type") {
//trim user name or typed name value to show in initial signature
const trimmedName = typedSignature
? typedSignature?.trim()
: currentUserName?.trim();
//get full name of user
const fullUserName = typedSignature || currentUserName
const firstCharacter = trimmedName?.charAt(0);
const userName =
currWidgetsDetails?.type === "initials"
? firstCharacter
: typedSignature;
: fullUserName;
const signatureValue = currWidgetsDetails?.typeSignature;
setTypedSignature(signatureValue || userName || "");
convertToImg(fontSelect, userName);
convertToImg(fontSelect, signatureValue || userName || "");
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isTab]);
@@ -1472,6 +1475,7 @@ function WidgetsValueModal(props) {
case cellsWidget:
return (
<CellsWidget
isEnabled={true}
count={cellsValue.length}
height="100%"
value={cellsValue.join("")}
@@ -1762,6 +1766,10 @@ function WidgetsValueModal(props) {
regexValidation = /^[0-9\s]*$/;
validateExpression(regexValidation);
break;
case "ssn":
regexValidation = /^(?!000|666|9\d{2})\d{3}-(?!00)\d{2}-(?!0000)\d{4}$/;
validateExpression(regexValidation);
break;
default:
// Grab the current pattern (if it exists)
const pattern = currWidgetsDetails?.options?.validation?.pattern;
@@ -1872,6 +1880,11 @@ function WidgetsValueModal(props) {
}
};
const handleclose = () => {
// If validation is shown, clear the response and close the modal
if (isShowValidation) {
handleClear();
setIsShowValidation(false);
}
dispatch(setIsShowModal({}));
dispatch(setLastIndex(""));
if (currWidgetsDetails?.type === textWidget && uniqueId) {
@@ -1936,7 +1949,7 @@ function WidgetsValueModal(props) {
<>
<ModalUi
isOpen={true}
handleClose={() => !isShowValidation && handleclose()}
handleClose={() => handleclose()}
position="bottom"
>
<div className="h-[100%] p-[18px]">
+31 -1
View File
@@ -98,6 +98,27 @@ export const getSecureUrl = async (url) => {
}
};
/**
* Removes a trailing path segment from a URL string, if present.
*
* @param {string} url - The original URL.
* @param {string} segment - The segment to strip off (default: "app").
* @returns {string} - The URL with the trailing segment removed, or unmodified if it didnt match.
*/
export function removeTrailingSegment(url, segment = "app") {
// Normalize a trailing slash (e.g. “/app/” → “/app”)
const normalized = url.endsWith("/") ? url.slice(0, -1) : url;
const lastSlash = normalized.lastIndexOf("/");
const lastPart = normalized.slice(lastSlash + 1);
if (lastPart === segment) {
return normalized.slice(0, lastSlash);
}
return normalized;
}
export const color = [
"#93a3db",
"#e6c3db",
@@ -3425,7 +3446,7 @@ export const handleCheckResponse = (checkUser, setminRequiredCount) => {
export const decryptPdf = async (file, password) => {
const name = generatePdfName(16);
const baseApi = localStorage.getItem("baseUrl") || "";
const url = baseApi.replace("/app", "") + "decryptpdf?ts=" + Date.now();
const url = removeTrailingSegment(baseApi) + "/decryptpdf?ts=" + Date.now();
let formData = new FormData();
formData.append("file", file);
formData.append("password", password);
@@ -3468,6 +3489,15 @@ export function base64ToFile(base64String, filename) {
return new File([u8arr], filename, { type: mime });
}
/**
* Reads the given File object and returns its contents as an ArrayBuffer.
*
* @param {File} file
* The File instance to be read.
* @returns {Promise<ArrayBuffer>}
* A promise that resolves with the files binary data as an ArrayBuffer,
* or rejects with an error if the read fails.
*/
export function getFileAsArrayBuffer(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
+3 -2
View File
@@ -16,7 +16,8 @@ import {
toDataUrl,
decryptPdf,
base64ToFile,
getFileAsArrayBuffer
getFileAsArrayBuffer,
removeTrailingSegment
} from "../constant/Utils";
import { PDFDocument } from "pdf-lib";
import axios from "axios";
@@ -240,7 +241,7 @@ const Forms = (props) => {
) {
try {
const baseApi = localStorage.getItem("baseUrl") || "";
const url = baseApi.replace("/app", "") + "docxtopdf";
const url = removeTrailingSegment(baseApi) + "/docxtopdf";
let fd = new FormData();
fd.append("file", file);
setfileload(true);
+2 -8
View File
@@ -1608,13 +1608,7 @@ function PlaceHolderSign() {
parseInt(defaultdata?.cellCount || 5)
),
validation:
isSubscribe && inputype
? {
type: inputype,
pattern:
inputype === "regex" ? defaultdata.textvalidate : ""
}
: {},
{},
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor:
@@ -2636,7 +2630,7 @@ function PlaceHolderSign() {
title={t("document-alert")}
showClose={false}
>
<div className="h-[100%] p-[20px]">
<div className="h-[100%] p-[20px] text-base-content">
<p>{isAlreadyPlace.message}</p>
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
<button
+1
View File
@@ -7,6 +7,7 @@ import axios from 'axios';
dotenv.config();
export const cloudServerUrl = 'http://localhost:8080/app';
export const serverAppId = process.env.APP_ID || 'opensign';
export const appName = 'OpenSign™';
export const MAX_NAME_LENGTH = 250;
@@ -3,7 +3,7 @@ import axios from 'axios';
import multer from 'multer';
import libre from 'libreoffice-convert';
import util from 'node:util';
import { cloudServerUrl, getSecureUrl } from '../../Utils.js';
import { cloudServerUrl, getSecureUrl, serverAppId } from '../../Utils.js';
libre.convertAsync = util.promisify(libre.convert);
@@ -29,7 +29,7 @@ function generatePdfName(length) {
export default async function docxtopdf(req, res) {
const serverUrl = cloudServerUrl;
const appId = process.env.APP_ID;
const appId = serverAppId;
const masterKey = process.env.MASTER_KEY;
const inputPath = req.file.path;
const name = generatePdfName(16);
@@ -3,7 +3,7 @@ import multer from 'multer';
import multerS3 from 'multer-s3';
import aws from 'aws-sdk';
import dotenv from 'dotenv';
import { cloudServerUrl, useLocal } from '../../Utils.js';
import { cloudServerUrl, serverAppId, useLocal } from '../../Utils.js';
dotenv.config();
function sanitizeFileName(fileName) {
@@ -50,7 +50,7 @@ async function uploadFile(req, res) {
const DO_SPACE = process.env.DO_SPACE;
const parseBaseUrl = cloudServerUrl; //process.env.SERVER_URL;
const parseAppId = process.env.APP_ID;
const parseAppId = serverAppId;
let fileStorage;
if (useLocal === 'true') {
fileStorage = multer.diskStorage({
@@ -1,7 +1,7 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
import { cloudServerUrl, serverAppId } from '../../Utils.js';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const APPID = serverAppId;
const masterKEY = process.env.MASTER_KEY;
async function addTeamAndOrg(extUser) {
try {
@@ -1,10 +1,10 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
import { cloudServerUrl, serverAppId } from '../../Utils.js';
async function AuthLoginAsMail(request) {
try {
//function for login user using user objectId without touching user's password
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const APPID = serverAppId;
const masterKEY = process.env.MASTER_KEY;
let otpN = request.params.otp;
@@ -1,5 +1,5 @@
import axios from 'axios';
import { appName, cloudServerUrl } from '../../Utils.js';
import { appName, cloudServerUrl, serverAppId } from '../../Utils.js';
export default async function forwardDoc(request) {
try {
@@ -58,7 +58,7 @@ export default async function forwardDoc(request) {
mailRes = await axios.post(`${cloudServerUrl}/functions/sendmailv3`, params, {
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Application-Id': serverAppId,
'X-Parse-Master-Key': process.env.MASTER_KEY,
},
});
@@ -1,5 +1,5 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
import { cloudServerUrl, serverAppId } from '../../Utils.js';
export default async function GetTemplate(request) {
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
@@ -10,7 +10,7 @@ export default async function GetTemplate(request) {
if (sessiontoken) {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Application-Id': serverAppId,
'X-Parse-Session-Token': sessiontoken,
},
});
@@ -1,6 +1,6 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
const appId = process.env.APP_ID;
import { cloudServerUrl, serverAppId } from '../../Utils.js';
const appId = serverAppId;
const masterkey = process.env.MASTER_KEY;
export default async function createBatchContact(req) {
if (!req?.user) {
@@ -1,7 +1,7 @@
import axios from 'axios';
import { cloudServerUrl, mailTemplate, replaceMailVaribles } from '../../Utils.js';
import { cloudServerUrl, mailTemplate, replaceMailVaribles, serverAppId } from '../../Utils.js';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
const appId = serverAppId;
async function deductcount(docsCount, extUserId) {
try {
const extCls = new Parse.Object('contracts_Users');
@@ -1,5 +1,5 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
import { cloudServerUrl, serverAppId } from '../../Utils.js';
export default async function getDocument(request) {
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const docId = request.params.docId;
@@ -30,7 +30,7 @@ export default async function getDocument(request) {
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Application-Id': serverAppId,
'X-Parse-Session-Token': sessiontoken,
},
});
@@ -1,8 +1,8 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
import { cloudServerUrl, serverAppId } from '../../Utils.js';
export default async function getDrive(request) {
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
const appId = serverAppId;
const limit = request.params.limit;
const skip = request.params.skip;
const docId = request.params.docId;
@@ -1,4 +1,4 @@
import { cloudServerUrl } from '../../Utils.js';
import { cloudServerUrl, serverAppId } from '../../Utils.js';
import reportJson from './reportsJson.js';
import axios from 'axios';
@@ -14,7 +14,7 @@ export default async function getReport(request) {
const searchTerm = request.params.searchTerm || '';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
const appId = serverAppId;
const masterKey = process.env.MASTER_KEY;
const sessionToken = request.headers['sessiontoken'] || request.headers['x-parse-session-token'];
try {
@@ -7,6 +7,7 @@ import {
saveFileUsage,
getSecureUrl,
appName,
serverAppId,
} from '../../../Utils.js';
import GenerateCertificate from './GenerateCertificate.js';
import { pdflibAddPlaceholder } from '@signpdf/placeholder-pdf-lib';
@@ -15,7 +16,7 @@ import { SignPdf } from '@signpdf/signpdf';
import { P12Signer } from '@signpdf/signer-p12';
const serverUrl = cloudServerUrl; // process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const APPID = serverAppId;
const masterKEY = process.env.MASTER_KEY;
const eSignName = 'OpenSign';
const eSigncontact = 'hello@opensignlabs.com';
@@ -1,7 +1,7 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
import { cloudServerUrl, serverAppId } from '../../Utils.js';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const APPID = serverAppId;
const masterKEY = process.env.MASTER_KEY;
async function saveUser(userDetails) {
+4 -4
View File
@@ -15,7 +15,7 @@ import AWS from 'aws-sdk';
import { app as customRoute } from './cloud/customRoute/customApp.js';
import { exec } from 'child_process';
import { createTransport } from 'nodemailer';
import { appName, cloudServerUrl, smtpenable, smtpsecure, useLocal } from './Utils.js';
import { appName, cloudServerUrl, serverAppId, smtpenable, smtpsecure, useLocal } from './Utils.js';
import { SSOAuth } from './auth/authadapter.js';
import createContactIndex from './migrationdb/createContactIndex.js';
import { validateSignedLocalUrl } from './cloud/parsefunction/getSignedUrl.js';
@@ -97,7 +97,7 @@ export const config = {
cloud: function () {
import('./cloud/main.js');
},
appId: process.env.APP_ID || 'opensign',
appId: serverAppId,
logLevel: ['error'],
maxLimit: 500,
maxUploadSize: '30mb',
@@ -234,8 +234,8 @@ if (!process.env.TESTING) {
createContactIndex();
const migrate = isWindows
? `set APPLICATION_ID=${process.env.APP_ID}&& set SERVER_URL=${cloudServerUrl}&& set MASTER_KEY=${process.env.MASTER_KEY}&& npx parse-dbtool migrate`
: `APPLICATION_ID=${process.env.APP_ID} SERVER_URL=${cloudServerUrl} MASTER_KEY=${process.env.MASTER_KEY} npx parse-dbtool migrate`;
? `set APPLICATION_ID=${serverAppId}&& set SERVER_URL=${cloudServerUrl}&& set MASTER_KEY=${process.env.MASTER_KEY}&& npx parse-dbtool migrate`
: `APPLICATION_ID=${serverAppId} SERVER_URL=${cloudServerUrl} MASTER_KEY=${process.env.MASTER_KEY} npx parse-dbtool migrate`;
exec(migrate, (error, stdout, stderr) => {
if (error) {
console.error(`Error: ${error.message}`);