Compare commits
4 Commits
feat/add-d
...
chore/remo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aecf2f32b9 | ||
|
|
b23967d777 | ||
|
|
7bcf5fbd86 | ||
|
|
7218b950fe |
@@ -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>
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ?? '')}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "User" ADD COLUMN "signature" TEXT;
|
||||
@@ -20,6 +20,7 @@ model User {
|
||||
emailVerified DateTime?
|
||||
password String?
|
||||
source String?
|
||||
signature String?
|
||||
identityProvider IdentityProvider @default(DOCUMENSO)
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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>;
|
||||
|
||||
Reference in New Issue
Block a user