diff --git a/README.md b/README.md index df0c6a76e..59ec81c3b 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ Documenso aims to be the world's most trusted document signing tool. This trust is built by empowering you to self-host Documenso and review how it works under the hood. Join us in creating the new internet of trust. +## Tools + +This repos uses 📝 https://gitmoji.dev/ for more expressive commit messages. + ## todos - Sendgrid setup and nodemailer-sendgrid SMTP hint (import for interchangeability like SMTP) diff --git a/apps/web/components/editor/field.tsx b/apps/web/components/editor/field.tsx index 60204bcd9..259a8ac0b 100644 --- a/apps/web/components/editor/field.tsx +++ b/apps/web/components/editor/field.tsx @@ -1,8 +1,11 @@ import { ResizableBox, ResizeCallbackData } from "react-resizable"; import React, { SyntheticEvent, useEffect, useState } from "react"; import Draggable from "react-draggable"; -import { CircleStackIcon } from "@heroicons/react/24/outline"; +import { CircleStackIcon, TrashIcon } from "@heroicons/react/24/solid"; import Logo from "../logo"; +import { IconButton } from "@documenso/ui"; +import toast from "react-hot-toast"; +import { XCircleIcon } from "@heroicons/react/20/solid"; const stc = require("string-to-color"); type FieldPropsType = { @@ -10,17 +13,21 @@ type FieldPropsType = { color: string; type: string; position: any; + positionX: number; + positionY: number; id: string; recipient: string; }; - onPositionChangedHandler: any; + onPositionChanged: any; + onDelete: any; }; 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 [position, setPosition]: any = useState({ + x: props.field.positionX, + y: props.field.positionY, + }); const nodeRef = React.createRef(); const onControlledDrag = (e: any, position: any) => { const { x, y } = position; @@ -31,7 +38,7 @@ export default function Field(props: FieldPropsType) { if (!position) return; const { x, y } = position; - props.onPositionChangedHandler({ x, y }, props.field.id); + props.onPositionChanged({ x, y }, props.field.id); }; return ( @@ -41,17 +48,29 @@ export default function Field(props: FieldPropsType) { position={position} onDrag={onControlledDrag} onStop={onDragStop} + defaultPosition={{ x: 0, y: 0 }} + cancel="strong" >
{/* todo icons */} - Signature + {field.type}
{props.field.recipient}
+ + { + props.onDelete(props.field.id); + }} + > +
); diff --git a/apps/web/components/editor/pdf-editor.tsx b/apps/web/components/editor/pdf-editor.tsx index 14f350f45..cbd3552d8 100644 --- a/apps/web/components/editor/pdf-editor.tsx +++ b/apps/web/components/editor/pdf-editor.tsx @@ -1,19 +1,166 @@ import { NEXT_PUBLIC_WEBAPP_URL } from "@documenso/lib/constants"; import { useRouter } from "next/router"; import dynamic from "next/dynamic"; -import React from "react"; +import React, { useState } from "react"; +import { Button } from "@documenso/ui"; +import short from "short-uuid"; +import toast from "react-hot-toast"; +import { FieldType } from "@prisma/client"; +const stc = require("string-to-color"); const PDFViewer = dynamic(() => import("./pdf-viewer"), { ssr: false, }); export default function PDFEditor(props: any) { + const [selectedValue, setSelectedValue] = useState(""); + const [fields, setFields] = useState(props.document.Field); const router = useRouter(); + function onPositionChangedHandler(position: any, id: any) { + if (!position) return; + const movedField = fields.find((e) => e.id == id); + movedField.positionX = position.x; + movedField.positionY = position.y; + upsertField(props.document, movedField); + + // no instant redraw neccessary, postion information for saving or later rerender is enough + // setFields(newFields); + } + + function onDeleteHandler(id: any) { + const field = fields.find((e) => e.id == id); + const fieldIndex = fields.map((item) => item.id).indexOf(id); + console.log(fieldIndex); + if (fieldIndex > -1) { + const fieldWithoutRemoved = [...fields]; + const removedField = fieldWithoutRemoved.splice(fieldIndex, 1); + setFields(fieldWithoutRemoved); + deleteField(field).catch((err) => { + setFields(fieldWithoutRemoved.concat(removedField)); + }); + } + } + return ( - + <> + + + + + + ); } + +async function upsertField(document: any, field: any): Promise { + try { + const created = await toast.promise( + fetch("/api/documents/" + document.id + "/fields", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(field), + }).then((res) => { + if (!res.ok) { + throw new Error(res.status.toString()); + } + return res.json(); + }), + { + loading: "Saving...", + success: "Saved.", + error: "Could not save :/", + }, + { + id: "saving field", + style: { + minWidth: "200px", + }, + } + ); + return created; + } catch (error) {} +} + +async function deleteField(field: any) { + if (!field.id) { + return; + } + + try { + const deleted = toast.promise( + fetch("/api/documents/" + 0 + "/fields/" + field.id, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(field), + }).then((res) => { + if (!res.ok) { + throw new Error(res.status.toString()); + } + return res; + }), + { + loading: "Deleting...", + success: "Deleted.", + error: "Could not delete :/", + }, + { + id: "delete", + style: { + minWidth: "200px", + }, + } + ); + return deleted; + } catch (error) {} +} diff --git a/apps/web/components/editor/pdf-viewer.jsx b/apps/web/components/editor/pdf-viewer.jsx index eaea14027..abb03bf0d 100644 --- a/apps/web/components/editor/pdf-viewer.jsx +++ b/apps/web/components/editor/pdf-viewer.jsx @@ -2,21 +2,18 @@ import { Fragment, useState } from "react"; import { Document, Page } from "react-pdf/dist/esm/entry.webpack5"; import Field from "./field"; import short from "short-uuid"; -import { Button } from "@documenso/ui"; -const stc = require("string-to-color"); export default function PDFViewer(props) { const [file, setFile] = useState(""); - const [selectedValue, setSelectedValue] = useState(""); const [numPages, setNumPages] = useState(null); 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); + props.onPositionChanged(position, id); + } + + function onDeleteHandler(id) { + props.onDelete(id); } function onFileChange(event) { @@ -36,50 +33,7 @@ export default function PDFViewer(props) { return ( <> ))} -