Compare commits

...
9 changed files with 90 additions and 35 deletions
@@ -12,12 +12,9 @@ function BorderResize(props) {
return ( return (
<div <div
style={{ style={{ width: getHeight() || "14px", height: getHeight() || "14px" }}
width: getHeight() || "14px",
height: getHeight() || "14px"
}}
className={`${props.right ? `-right-[12px]` : "-right-[2px]"} ${ 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]`} } absolute inline-block hover:cursor-sw-resize border-r-[3px] border-b-[3px] border-[#188ae2]`}
></div> ></div>
); );
@@ -1,6 +1,7 @@
import { useState, useEffect, useRef } from "react"; import { useState, useEffect, useRef } from "react";
const Cell = ({ const Cell = ({
isEnabled,
count, count,
h, h,
value, value,
@@ -15,10 +16,11 @@ const Cell = ({
hint hint
}) => ( }) => (
<div <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 }} style={{ flex: `0 0 ${100 / count}%`, height: h }}
> >
<input <input
disabled={!isEnabled}
maxLength={1} maxLength={1}
value={value} value={value}
readOnly={!editable} readOnly={!editable}
@@ -27,7 +29,7 @@ const Cell = ({
onKeyDown={editable ? (e) => onKeyDown && onKeyDown(e, index) : undefined} onKeyDown={editable ? (e) => onKeyDown && onKeyDown(e, index) : undefined}
// trigger validation when leaving a cell // trigger validation when leaving a cell
onBlur={editable ? (e) => onBlur && onBlur(e, index) : undefined} 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} placeholder={hint}
style={{ fontFamily: "Arial, sans-serif", fontSize, color: fontColor }} style={{ fontFamily: "Arial, sans-serif", fontSize, color: fontColor }}
/> />
@@ -35,6 +37,7 @@ const Cell = ({
); );
export default function CellsWidget({ export default function CellsWidget({
isEnabled,
count = 8, count = 8,
height = 40, height = 40,
value = "", value = "",
@@ -120,6 +123,7 @@ export default function CellsWidget({
{cells.map((val, i) => ( {cells.map((val, i) => (
<Cell <Cell
key={i} key={i}
isEnabled={isEnabled}
count={cellCount} count={cellCount}
h={height} h={height}
value={val} value={val}
@@ -740,7 +740,7 @@ function Placeholder(props) {
return "not-allowed"; return "not-allowed";
} }
} else { } else {
return "all-scroll"; return "move";
} }
}; };
@@ -946,23 +946,24 @@ function Placeholder(props) {
props.pos.key === props?.currWidgetsDetails?.key && <BorderResize /> 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. {/* 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 ouline for all widgets. 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 ouline: 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 ouline. 3.1- When isAlllowModify is true, show border.
3.2- Do not display ouline for widgets already assigned (props.assignedWidgetId.includes(props.pos.key) is true). 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 <div
className={`${ className="flex items-stretch justify-center"
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`}
style={{ style={{
outlineColor: themeColor,
left: xPos(props.pos, props.isSignYourself), left: xPos(props.pos, props.isSignYourself),
top: yPos(props.pos, props.isSignYourself), top: yPos(props.pos, props.isSignYourself),
width: "100%", width: "100%",
@@ -226,6 +226,7 @@ function PlaceholderType(props) {
props.isPlaceholder || props.isSignYourself || props.isSelfSign; props.isPlaceholder || props.isSignYourself || props.isSelfSign;
return ( return (
<CellsWidget <CellsWidget
isEnabled={iswidgetEnable}
count={count} count={count}
height={height} height={height}
value={cells.join("")} 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) => { const handledefaultChange = (e) => {
if (formdata.textvalidate) { if (formdata.textvalidate) {
const regexObject = RegexParser(handleValidation(formdata.textvalidate)); const regexObject = RegexParser(handleValidation(formdata.textvalidate));
@@ -148,7 +165,7 @@ const WidgetNameModal = (props) => {
//allow space in text regex //allow space in text regex
return "/^[a-zA-Z ]+$/"; return "/^[a-zA-Z ]+$/";
case "ssn": case "ssn":
return "^\\d{3}-\\d{2}-\\d{4}$"; return "/^(?!000|666|9\\d{2})\\d{3}-(?!00)\\d{2}-(?!0000)\\d{4}$/";
default: default:
return type; return type;
} }
@@ -1472,6 +1472,7 @@ function WidgetsValueModal(props) {
case cellsWidget: case cellsWidget:
return ( return (
<CellsWidget <CellsWidget
isEnabled={true}
count={cellsValue.length} count={cellsValue.length}
height="100%" height="100%"
value={cellsValue.join("")} value={cellsValue.join("")}
@@ -1762,6 +1763,10 @@ function WidgetsValueModal(props) {
regexValidation = /^[0-9\s]*$/; regexValidation = /^[0-9\s]*$/;
validateExpression(regexValidation); validateExpression(regexValidation);
break; break;
case "ssn":
regexValidation = /^(?!000|666|9\d{2})\d{3}-(?!00)\d{2}-(?!0000)\d{4}$/;
validateExpression(regexValidation);
break;
default: default:
// Grab the current pattern (if it exists) // Grab the current pattern (if it exists)
const pattern = currWidgetsDetails?.options?.validation?.pattern; const pattern = currWidgetsDetails?.options?.validation?.pattern;
@@ -1872,6 +1877,11 @@ function WidgetsValueModal(props) {
} }
}; };
const handleclose = () => { const handleclose = () => {
// If validation is shown, clear the response and close the modal
if (isShowValidation) {
handleClear();
setIsShowValidation(false);
}
dispatch(setIsShowModal({})); dispatch(setIsShowModal({}));
dispatch(setLastIndex("")); dispatch(setLastIndex(""));
if (currWidgetsDetails?.type === textWidget && uniqueId) { if (currWidgetsDetails?.type === textWidget && uniqueId) {
@@ -1936,7 +1946,7 @@ function WidgetsValueModal(props) {
<> <>
<ModalUi <ModalUi
isOpen={true} isOpen={true}
handleClose={() => !isShowValidation && handleclose()} handleClose={() => handleclose()}
position="bottom" position="bottom"
> >
<div className="h-[100%] p-[18px]"> <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 didn’t 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 = [ export const color = [
"#93a3db", "#93a3db",
"#e6c3db", "#e6c3db",
@@ -3425,7 +3446,7 @@ export const handleCheckResponse = (checkUser, setminRequiredCount) => {
export const decryptPdf = async (file, password) => { export const decryptPdf = async (file, password) => {
const name = generatePdfName(16); const name = generatePdfName(16);
const baseApi = localStorage.getItem("baseUrl") || ""; 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(); let formData = new FormData();
formData.append("file", file); formData.append("file", file);
formData.append("password", password); formData.append("password", password);
@@ -3468,6 +3489,15 @@ export function base64ToFile(base64String, filename) {
return new File([u8arr], filename, { type: mime }); 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 file’s binary data as an ArrayBuffer,
* or rejects with an error if the read fails.
*/
export function getFileAsArrayBuffer(file) { export function getFileAsArrayBuffer(file) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const reader = new FileReader(); const reader = new FileReader();
+3 -2
View File
@@ -16,7 +16,8 @@ import {
toDataUrl, toDataUrl,
decryptPdf, decryptPdf,
base64ToFile, base64ToFile,
getFileAsArrayBuffer getFileAsArrayBuffer,
removeTrailingSegment
} from "../constant/Utils"; } from "../constant/Utils";
import { PDFDocument } from "pdf-lib"; import { PDFDocument } from "pdf-lib";
import axios from "axios"; import axios from "axios";
@@ -240,7 +241,7 @@ const Forms = (props) => {
) { ) {
try { try {
const baseApi = localStorage.getItem("baseUrl") || ""; const baseApi = localStorage.getItem("baseUrl") || "";
const url = baseApi.replace("/app", "") + "docxtopdf"; const url = removeTrailingSegment(baseApi) + "/docxtopdf";
let fd = new FormData(); let fd = new FormData();
fd.append("file", file); fd.append("file", file);
setfileload(true); setfileload(true);
+2 -8
View File
@@ -1608,13 +1608,7 @@ function PlaceHolderSign() {
parseInt(defaultdata?.cellCount || 5) parseInt(defaultdata?.cellCount || 5)
), ),
validation: validation:
isSubscribe && inputype {},
? {
type: inputype,
pattern:
inputype === "regex" ? defaultdata.textvalidate : ""
}
: {},
fontSize: fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || 12, fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor: fontColor:
@@ -2636,7 +2630,7 @@ function PlaceHolderSign() {
title={t("document-alert")} title={t("document-alert")}
showClose={false} showClose={false}
> >
<div className="h-[100%] p-[20px]"> <div className="h-[100%] p-[20px] text-base-content">
<p>{isAlreadyPlace.message}</p> <p>{isAlreadyPlace.message}</p>
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div> <div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
<button <button