fields ui

This commit is contained in:
Timur Ercan
2023-02-09 15:48:26 +01:00
parent 4cb9a3a65a
commit c4eb1b27d9
5 changed files with 106 additions and 52 deletions

View File

@ -1,38 +1,60 @@
import { ResizableBox, ResizeCallbackData } from "react-resizable"; import { ResizableBox, ResizeCallbackData } from "react-resizable";
import React, { SyntheticEvent, useState } from "react"; import React, { SyntheticEvent, useEffect, useState } from "react";
import Draggable from "react-draggable"; import Draggable from "react-draggable";
import { CircleStackIcon } from "@heroicons/react/24/outline"; import { CircleStackIcon } from "@heroicons/react/24/outline";
import Logo from "../logo"; import Logo from "../logo";
const stc = require("string-to-color");
type FieldPropsType = { type FieldPropsType = {
field: {
color: string; color: string;
type: string; type: string;
position: any;
id: string;
recipient: string;
};
onPositionChangedHandler: any;
}; };
export default function Field(props: FieldPropsType) { export default function Field(props: FieldPropsType) {
const [field, setField]: any = useState(props.field);
const [position, setPosition]: any = useState(
props.field.position || { x: 0, y: -842 }
);
const nodeRef = React.createRef<HTMLDivElement>();
console.log(props.field);
const onControlledDrag = (e: any, position: any) => {
const { x, y } = position;
setPosition({ x, y });
};
const onDragStop = (e: any, position: any) => {
if (!position) return;
const { x, y } = position;
props.onPositionChangedHandler({ x, y }, props.field.id);
};
return ( return (
<Draggable bounds="parent" defaultPosition={{ x: 0, y: -595 }}> <Draggable
<ResizableBox nodeRef={nodeRef}
width={150} bounds="parent"
height={75} position={position}
minConstraints={[170, 75]} onDrag={onControlledDrag}
maxConstraints={[260, 125]} onStop={onDragStop}
className="bg-neon opacity-90 w-auto h-auto flex align-middle" >
lockAspectRatio={true} <div
onResizeStart={(e: SyntheticEvent, data: ResizeCallbackData) => { ref={nodeRef}
e.preventDefault(); style={{ background: stc(props.field.recipient) }}
e.stopPropagation(); className="cursor-move opacity-90 p-2 m-auto w-auto flex-row-reverse text-lg font-bold text-center absolute"
}}
> >
<div className="m-auto w-auto flex-row-reverse text-lg font-bold text-center"> <div className="m-auto w-auto flex-row-reverse text-lg font-bold text-center">
{/* todo icons */} {/* todo icons */}
Signature Signature
<div className="text-xs text-center"> <div className="text-xs text-center">{props.field.recipient}</div>
Timur Ercan <br></br>
{"<timur.ercan31@gmail.com>"}
</div> </div>
</div> </div>
</ResizableBox>
</Draggable> </Draggable>
); );
} }

View File

@ -2,11 +2,22 @@ import { Fragment, useState } from "react";
import { Document, Page } from "react-pdf/dist/esm/entry.webpack5"; import { Document, Page } from "react-pdf/dist/esm/entry.webpack5";
import Field from "./editor/field"; import Field from "./editor/field";
import short from "short-uuid"; import short from "short-uuid";
import { Button } from "@documenso/ui";
const stc = require("string-to-color");
export default function PDFViewer(props) { export default function PDFViewer(props) {
const [file, setFile] = useState(""); const [file, setFile] = useState("");
const [selectedValue, setSelectedValue] = useState("");
const [numPages, setNumPages] = useState(null); const [numPages, setNumPages] = useState(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [fields, setFields] = useState([]);
function onPositionChangedHandler(position, id) {
if (!position) return;
const newFields = [...fields];
fields.find((e) => e.id == id).position = position;
// setFields(newFields);
}
function onFileChange(event) { function onFileChange(event) {
setFile(event.target.files[0]); setFile(event.target.files[0]);
@ -25,6 +36,50 @@ export default function PDFViewer(props) {
return ( return (
<> <>
<div hidden={loading}> <div hidden={loading}>
<div className="max-w-xs mt-6">
<select
className="mb-3 inline mt-1 w-full rounded-md border-gray-300 py-2 pl-3 pr-10 text-base focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
style={{ background: stc(selectedValue) }}
defaultValue={props?.document?.Recipient[0]}
value={selectedValue}
selectedIndex={0}
onChange={(e) => setSelectedValue(e.target.value)}
>
{props?.document?.Recipient?.map((item) => (
<option
key={item.email + short.generate().toString()}
style={{
background: stc(
item.name ? `${item.name} <${item.email}>` : item.email
),
}}
>
{item.name ? `${item.name} <${item.email}>` : item.email}
</option>
))}
</select>
</div>
<Button
className="inline ml-1"
onClick={() => {
setFields(
fields.concat({
id: short.generate().toString(),
type: "signature",
position: { x: 0, y: -842 },
recipient: selectedValue,
})
);
}}
>
Add Signature Field
</Button>
<Button color="secondary" className="inline ml-1">
Add Date Field
</Button>
<Button color="secondary" className="inline ml-1">
Add Text Field
</Button>
<Document <Document
file={props.pdfUrl} file={props.pdfUrl}
onLoadSuccess={onDocumentLoadSuccess} onLoadSuccess={onDocumentLoadSuccess}
@ -54,11 +109,12 @@ export default function PDFViewer(props) {
onLoadSuccess={() => setLoading(false)} onLoadSuccess={() => setLoading(false)}
onRenderError={() => setLoading(false)} onRenderError={() => setLoading(false)}
></Page> ></Page>
{props.fields.map((item) => ( {fields.map((item) => (
<Field <Field
key={short.generate().toString()} key={item.id}
position={{ x: 0, y: -500 }} field={item}
className="absolute" className="absolute"
onPositionChangedHandler={onPositionChangedHandler}
></Field> ></Field>
))} ))}
</div> </div>

View File

@ -43,6 +43,7 @@
"sass": "^1.57.1", "sass": "^1.57.1",
"short-uuid": "^4.2.2", "short-uuid": "^4.2.2",
"signature_pad": "^4.1.4", "signature_pad": "^4.1.4",
"string-to-color": "^2.2.2",
"typescript": "4.8.4" "typescript": "4.8.4"
}, },
"devDependencies": { "devDependencies": {

View File

@ -23,7 +23,6 @@ const PDFViewer = dynamic(() => import("../../../components/pdf-viewer"), {
const DocumentsDetailPage: NextPageWithLayout = (props: any) => { const DocumentsDetailPage: NextPageWithLayout = (props: any) => {
const router = useRouter(); const router = useRouter();
const [fields, setFields]: any[] = useState([]);
return ( return (
<div className="mt-4"> <div className="mt-4">
@ -95,34 +94,8 @@ const DocumentsDetailPage: NextPageWithLayout = (props: any) => {
</div> </div>
</div> </div>
<div className="mx-auto w-fit"> <div className="mx-auto w-fit">
<div className="max-w-xs mt-6">
<select
className="mb-3 inline mt-1 w-full rounded-md border-gray-300 py-2 pl-3 pr-10 text-base focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
defaultValue={props?.document?.Recipient[0]}
>
{props?.document?.Recipient?.map((item: any) => (
<option key={item.email + short.generate().toString()}>
{item.name ? `${item.name} <${item.email}>` : item.email}
</option>
))}
</select>
<Button
className="inline ml-1"
onClick={() => {
setFields(fields.concat({ type: "signature" }));
}}
>
Add Signature
</Button>
<Button color="secondary" className="inline ml-1">
Add Date
</Button>
<Button color="secondary" className="inline ml-1">
Add Text
</Button>
</div>
<PDFViewer <PDFViewer
fields={fields} document={props.document}
pdfUrl={`${NEXT_PUBLIC_WEBAPP_URL}/api/documents/${router.query.id}`} pdfUrl={`${NEXT_PUBLIC_WEBAPP_URL}/api/documents/${router.query.id}`}
/> />
</div> </div>

4
package-lock.json generated
View File

@ -76,6 +76,7 @@
"sass": "^1.57.1", "sass": "^1.57.1",
"short-uuid": "^4.2.2", "short-uuid": "^4.2.2",
"signature_pad": "^4.1.4", "signature_pad": "^4.1.4",
"string-to-color": "^2.2.2",
"typescript": "4.8.4" "typescript": "4.8.4"
}, },
"devDependencies": { "devDependencies": {
@ -8538,7 +8539,7 @@
"@types/nodemailer-sendgrid": "^1.0.0", "@types/nodemailer-sendgrid": "^1.0.0",
"@types/react-dom": "18.0.9", "@types/react-dom": "18.0.9",
"@types/react-pdf": "^6.2.0", "@types/react-pdf": "^6.2.0",
"@types/react-resizable": "*", "@types/react-resizable": "^3.0.3",
"autoprefixer": "^10.4.13", "autoprefixer": "^10.4.13",
"avatar-from-initials": "^1.0.3", "avatar-from-initials": "^1.0.3",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
@ -8566,6 +8567,7 @@
"sass": "^1.57.1", "sass": "^1.57.1",
"short-uuid": "^4.2.2", "short-uuid": "^4.2.2",
"signature_pad": "^4.1.4", "signature_pad": "^4.1.4",
"string-to-color": "*",
"tailwindcss": "^3.2.4", "tailwindcss": "^3.2.4",
"typescript": "4.8.4" "typescript": "4.8.4"
}, },