Compare commits

..

4 Commits

Author SHA1 Message Date
Catalin Pit
aecf2f32b9 chore: removed one more console.log 2023-09-04 16:41:28 +03:00
Catalin Pit
b23967d777 chore: removed console.logs and warn 2023-09-04 16:08:22 +03:00
Mythie
7bcf5fbd86 feat: store signature on signup 2023-09-01 19:46:44 +10:00
Mythie
7218b950fe feat: store profile signature 2023-09-01 18:43:53 +10:00
16 changed files with 40 additions and 99 deletions

View File

@@ -1,42 +0,0 @@
---
title: Design System
---
# We're building a beautiful, open-source alternative to DocuSign
> Read more about our design culture here:
>
> [https://documenso.com/blog/design-system](https://documenso.com/blog/design-system)
At Documenso, we aim to be a design-driven company.
We believe that design isn't just about how things look, but also how they work. We want to make sure that the product is easy to use and intuitive. We also want to ensure that the website, desktop and mobile apps are consistent and look and feel like they belong together.
To achieve this, we've created Documenso's design system containing tokens, primitives, and components, screens, and brand assets.
We're open-sourcing this design system so you can see how we build the product and think about design as a whole.
## Check out the design system
<iframe
src="https://documen.so/design-system-embed"
className="aspect-square w-full border-none"
frameBorder="0"
/>
## Remix and Share the community version on Figma
<a href="documen.so/design" target="_blank">
<figure>
<MdxNextImage
src="/blog/designsystem.png"
width="1260"
height="630"
alt="Documenso's Design System"
/>
<figcaption className="text-center">
Documenso's Design System ✨
</figcaption>
</figure>
</a>

View File

@@ -21,7 +21,6 @@ const FOOTER_LINKS = [
{ href: '/open', text: 'Open' },
{ href: 'https://shop.documenso.com', text: 'Shop', target: '_blank' },
{ href: 'https://status.documenso.com', text: 'Status', target: '_blank' },
{ href: '/design-system', text: 'Design' },
{ href: 'mailto:support@documenso.com', text: 'Support' },
{ href: '/privacy', text: 'Privacy' },
];
@@ -44,7 +43,7 @@ export const Footer = ({ className, ...props }: FooterProps) => {
</div>
</div>
<div className="grid max-w-xs flex-1 grid-cols-2 gap-x-4 gap-y-2">
<div className="flex flex-wrap items-center gap-x-4 gap-y-2.5">
{FOOTER_LINKS.map((link, index) => (
<Link
key={index}

View File

@@ -3,6 +3,7 @@ import { notFound } from 'next/navigation';
import { match } from 'ts-pattern';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { getServerComponentSession } from '@documenso/lib/next-auth/get-server-session';
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
import { viewedDocument } from '@documenso/lib/server-only/document/viewed-document';
import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-for-token';
@@ -42,10 +43,12 @@ export default async function SigningPage({ params: { token } }: SigningPageProp
return notFound();
}
const user = await getServerComponentSession();
const documentUrl = `data:application/pdf;base64,${document.document}`;
return (
<SigningProvider email={recipient.email} fullName={recipient.name}>
<SigningProvider email={recipient.email} fullName={recipient.name} signature={user?.signature}>
<div className="mx-auto w-full max-w-screen-xl px-4 md:px-8">
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={document.title}>
{document.title}

View File

@@ -28,9 +28,9 @@ export const useRequiredSigningContext = () => {
};
export interface SigningProviderProps {
fullName?: string;
email?: string;
signature?: string;
fullName?: string | null;
email?: string | null;
signature?: string | null;
children: React.ReactNode;
}

View File

@@ -118,6 +118,7 @@ export const ProfileForm = ({ className, user }: ProfileFormProps) => {
render={({ field: { onChange } }) => (
<SignaturePad
className="h-44 w-full rounded-lg border bg-white backdrop-blur-sm dark:border-[#e2d7c5] dark:bg-[#fcf8ee]"
defaultValue={user.signature ?? undefined}
onChange={(v) => onChange(v ?? '')}
/>
)}

View File

@@ -3,13 +3,14 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { Loader } from 'lucide-react';
import { signIn } from 'next-auth/react';
import { useForm } from 'react-hook-form';
import { Controller, useForm } from 'react-hook-form';
import { z } from 'zod';
import { TRPCClientError } from '@documenso/trpc/client';
import { trpc } from '@documenso/trpc/react';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { FormErrorMessage } from '@documenso/ui/primitives/form/form-error-message';
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import { SignaturePad } from '@documenso/ui/primitives/signature-pad';
@@ -19,6 +20,7 @@ export const ZSignUpFormSchema = z.object({
name: z.string().min(1),
email: z.string().email().min(1),
password: z.string().min(6).max(72),
signature: z.string().min(1, { message: 'We need your signature to sign documents' }),
});
export type TSignUpFormSchema = z.infer<typeof ZSignUpFormSchema>;
@@ -31,6 +33,7 @@ export const SignUpForm = ({ className }: SignUpFormProps) => {
const { toast } = useToast();
const {
control,
register,
handleSubmit,
formState: { errors, isSubmitting },
@@ -39,15 +42,16 @@ export const SignUpForm = ({ className }: SignUpFormProps) => {
name: '',
email: '',
password: '',
signature: '',
},
resolver: zodResolver(ZSignUpFormSchema),
});
const { mutateAsync: signup } = trpc.auth.signup.useMutation();
const onFormSubmit = async ({ name, email, password }: TSignUpFormSchema) => {
const onFormSubmit = async ({ name, email, password, signature }: TSignUpFormSchema) => {
try {
await signup({ name, email, password });
await signup({ name, email, password, signature });
await signIn('credentials', {
email,
@@ -119,8 +123,19 @@ export const SignUpForm = ({ className }: SignUpFormProps) => {
</Label>
<div>
<SignaturePad className="mt-2 h-36 w-full rounded-lg border bg-white dark:border-[#e2d7c5] dark:bg-[#fcf8ee]" />
<Controller
control={control}
name="signature"
render={({ field: { onChange } }) => (
<SignaturePad
className="mt-2 h-36 w-full rounded-lg border bg-white dark:border-[#e2d7c5] dark:bg-[#fcf8ee]"
onChange={(v) => onChange(v ?? '')}
/>
)}
/>
</div>
<FormErrorMessage className="mt-1.5" error={errors.signature} />
</div>
<Button size="lg" disabled={isSubmitting} className="dark:bg-documenso dark:hover:opacity-90">

View File

@@ -83,10 +83,7 @@ export const completeDocumentWithToken = async ({
},
});
console.log('documents', documents);
if (documents.count > 0) {
console.log('sealing document');
await sealDocument({ documentId: document.id });
}
};

View File

@@ -53,10 +53,6 @@ export const sealDocument = async ({ documentId }: SealDocumentOptions) => {
const doc = await PDFDocument.load(pdfData);
for (const field of fields) {
console.log('inserting field', {
...field,
Signature: null,
});
await insertFieldInPDF(doc, field);
}

View File

@@ -35,15 +35,6 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
const fieldX = pageWidth * (Number(field.positionX) / 100);
const fieldY = pageHeight * (Number(field.positionY) / 100);
console.log({
fieldWidth,
fieldHeight,
fieldX,
fieldY,
pageWidth,
pageHeight,
});
const font = await pdf.embedFont(isSignatureField ? fontCaveat : StandardFonts.Helvetica);
if (field.type === FieldType.SIGNATURE || field.type === FieldType.FREE_SIGNATURE) {
@@ -75,15 +66,6 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
// Invert the Y axis since PDFs use a bottom-left coordinate system
imageY = pageHeight - imageY - imageHeight;
console.log({
initialDimensions,
scalingFactor,
imageWidth,
imageHeight,
imageX,
imageY,
});
page.drawImage(image, {
x: imageX,
y: imageY,
@@ -107,17 +89,6 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
const textX = fieldX + (fieldWidth - textWidth) / 2;
let textY = fieldY + (fieldHeight - textHeight) / 2;
console.log({
initialDimensions,
scalingFactor,
textWidth,
textHeight,
textX,
textY,
pageWidth,
pageHeight,
});
// Invert the Y axis since PDFs use a bottom-left coordinate system
textY = pageHeight - textY - textHeight;

View File

@@ -9,9 +9,10 @@ export interface CreateUserOptions {
name: string;
email: string;
password: string;
signature?: string | null;
}
export const createUser = async ({ name, email, password }: CreateUserOptions) => {
export const createUser = async ({ name, email, password, signature }: CreateUserOptions) => {
const hashedPassword = await hash(password, SALT_ROUNDS);
const userExists = await prisma.user.findFirst({
@@ -29,6 +30,7 @@ export const createUser = async ({ name, email, password }: CreateUserOptions) =
name,
email: email.toLowerCase(),
password: hashedPassword,
signature,
identityProvider: IdentityProvider.DOCUMENSO,
},
});

View File

@@ -6,12 +6,7 @@ export type UpdateProfileOptions = {
signature: string;
};
export const updateProfile = async ({
userId,
name,
// TODO: Actually use signature
signature: _signature,
}: UpdateProfileOptions) => {
export const updateProfile = async ({ userId, name, signature }: UpdateProfileOptions) => {
// Existence check
await prisma.user.findFirstOrThrow({
where: {
@@ -25,7 +20,7 @@ export const updateProfile = async ({
},
data: {
name,
// signature,
signature,
},
});

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "User" ADD COLUMN "signature" TEXT;

View File

@@ -20,6 +20,7 @@ model User {
emailVerified DateTime?
password String?
source String?
signature String?
identityProvider IdentityProvider @default(DOCUMENSO)
accounts Account[]
sessions Session[]

View File

@@ -4,7 +4,7 @@ const { fontFamily } = require('tailwindcss/defaultTheme');
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ['class'],
content: ['src/**/*.{ts,tsx}', 'content/**/*.{md,mdx}'],
content: ['src/**/*.{ts,tsx}'],
theme: {
extend: {
fontFamily: {

View File

@@ -8,9 +8,9 @@ import { ZSignUpMutationSchema } from './schema';
export const authRouter = router({
signup: procedure.input(ZSignUpMutationSchema).mutation(async ({ input }) => {
try {
const { name, email, password } = input;
const { name, email, password, signature } = input;
return await createUser({ name, email, password });
return await createUser({ name, email, password, signature });
} catch (err) {
console.error(err);

View File

@@ -4,6 +4,7 @@ export const ZSignUpMutationSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
password: z.string().min(6),
signature: z.string().min(1, { message: 'A signature is required.' }),
});
export type TSignUpMutationSchema = z.infer<typeof ZSignUpMutationSchema>;