Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
815772d
fix: fixing few things, and getting started with securities endpoint
dahal Jul 2, 2024
6d7c28a
feat: getting started with restful SAFE api endpoint
dahal Jul 3, 2024
e6dd997
Merge branch 'main' into api/safe
dahal Jul 5, 2024
1d432ec
chore: update cmd+k text
dahal Jul 5, 2024
33b713d
WIP
dahal Jul 7, 2024
b85a59a
Merge branch 'main' into api/safe
dahal Jul 7, 2024
4b57872
feat: getting started with updated safe form
dahal Jul 8, 2024
6d806a2
WIP
dahal Jul 8, 2024
fffc572
feat: create a re-usable stakeholder-selector
dahal Jul 9, 2024
34baf5f
feat: mfn and pro-rata selector
dahal Jul 9, 2024
6a02f08
fix: mfn and proRata inputs
dahal Jul 10, 2024
0be26e1
Merge branch 'main' into api/safe
dahal Jul 10, 2024
68179e2
feat: add missing migration
dahal Jul 10, 2024
be9caeb
feat: rename tldr to message
dahal Jul 10, 2024
1cbbabe
chore: update missing <Tldr> to <Message>
dahal Jul 10, 2024
a03e654
feat: custom design a SAFE template
dahal Jul 13, 2024
4df4336
chore: seperate pages by hr block
dahal Jul 13, 2024
8b2bf82
chore: complete SAFE template, TODO - sender details
dahal Jul 13, 2024
c57c4a9
WIP
dahal Jul 17, 2024
5ecf409
Merge branch 'main' into api/safe
dahal Jul 17, 2024
51e8dee
feat: create SAFE preview slideover
dahal Jul 18, 2024
dfa622e
feat: get rid of un-necessary safe new/preview stuff
dahal Jul 18, 2024
4ac4cbc
feat: update stakeholder query to get just the investor
dahal Jul 18, 2024
3553ee3
feat: add a feature to reload investor list if it does not auto-populate
dahal Jul 18, 2024
e8b6663
feat: model for bank account
dahal Jul 19, 2024
78e8c26
feat: add error state to EmptyState component
dahal Jul 19, 2024
1d9feb2
feat: getting started with bank account settings
dahal Jul 19, 2024
222a53c
Merge branch 'main' into api/safe
dahal Jul 19, 2024
c6d5844
Merge branch 'main' into api/safe
dahal Jul 19, 2024
9b6852b
feat: move existing selectors to selector folder, and add bank-accoun…
dahal Jul 19, 2024
25259ba
fix: return bankAccountId onSelect
dahal Jul 19, 2024
7e3e367
chore: update BankAccountSelectorType
dahal Jul 19, 2024
35c46e7
feat: complete team member selector
dahal Jul 19, 2024
dfa00f1
chore: remove console.log
dahal Jul 20, 2024
076c3ef
Merge branch 'main' into api/safe
dahal Jul 24, 2024
1f19b3b
feat: add company rep and bank account selector to safe modal
dahal Jul 24, 2024
a390c00
chore: add help text and reorder inputs
dahal Jul 24, 2024
703cdef
chore: minor cleanups
dahal Jul 24, 2024
f3a4b44
feat: add tooltip with fields descriptions
dahal Jul 24, 2024
786d04f
chore: add endpint descriptions
dahal Jul 26, 2024
2d0cef6
Merge branch 'main' into feat/safe
dahal Aug 7, 2024
a335eb0
feat: getting started with safe APIs
dahal Aug 8, 2024
1341e07
feat: complete getOne and getMany SAFE api
dahal Aug 8, 2024
4925057
chore: minor cleanups
dahal Aug 8, 2024
9bf060d
WIP
dahal Aug 14, 2024
01690d8
Merge branch 'main' into feat/safe
G3root Sep 19, 2024
fff59f3
fix: lock file
G3root Sep 19, 2024
79b77e5
feat: update schema
G3root Sep 20, 2024
cb030b2
feat: add endpoint creator
G3root Sep 24, 2024
799a30b
fix: script strategy
G3root Sep 24, 2024
d5c1475
fix: auth
G3root Sep 24, 2024
597bd6f
chore: fix schema
G3root Sep 25, 2024
882fd8f
feat: update api routes
G3root Sep 25, 2024
b592836
fix: api
G3root Sep 26, 2024
e49ce5b
fix: error handling
G3root Sep 26, 2024
0465931
feat: update schema
G3root Sep 27, 2024
5743397
feat: add email layout
G3root Sep 27, 2024
fe4c332
chore: fix script
G3root Sep 27, 2024
d1f28e9
feat: add logo
G3root Sep 27, 2024
568f5b5
feat: add logo component
G3root Sep 27, 2024
2869176
feat: add safe signing email
G3root Sep 27, 2024
e9f3321
feat: add safe signing email
G3root Sep 27, 2024
b640655
feat: update schema
G3root Sep 30, 2024
6d20478
feat: safe
G3root Oct 2, 2024
9ccff80
feat: link document
G3root Oct 7, 2024
f117e7f
feat: add signature
G3root Oct 7, 2024
63fc004
feat: add close modal
G3root Oct 7, 2024
36df242
fix: type
G3root Oct 7, 2024
f10f192
feat: add migration
G3root Oct 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: safe
  • Loading branch information
G3root committed Oct 2, 2024
commit 6d20478d17aee78e8c55e24a37c332eac96b3ba0
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
"lodash-es": "^4.17.21",
"mime": "^4.0.3",
"nanoid": "^5.0.4",
"next": "^14.2.5",
"next": "^14.2.14",
"next-auth": "^4.24.7",
"next-nprogress-bar": "^2.3.13",
"nodemailer": "^6.9.14",
Expand Down
116 changes: 58 additions & 58 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

19 changes: 9 additions & 10 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -848,11 +848,11 @@ model Safe {
companyId String
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)

signer SafeSignerMember @relation(fields: [signerMemberId], references: [id])
signerMemberId String
signerMember SafeSignerMember @relation(fields: [signerMemberId], references: [id])
signerMemberId String @unique

signerStakeholder SafeSignerStakeholder @relation(fields: [signerStakeholderId], references: [id])
signerStakeholderId String
signerStakeholderId String @unique

bankAccountId String
bankAccount BankAccount @relation(fields: [bankAccountId], references: [id], onDelete: Cascade)
Expand All @@ -870,11 +870,11 @@ model Safe {
}

model SafeSignerMember {
id String @id @default(cuid())

id String @id @default(cuid())
status SafeSigningStatus @default(PENDING)
member Member @relation(fields: [memberId], references: [id])
memberId String
Safe Safe[]
safe Safe?
fields DocumentCustomField[]
SafeSigningToken SafeSigningToken[]

Expand All @@ -891,17 +891,16 @@ model SafeSignerStakeholder {
status SafeSigningStatus @default(PENDING)
stakeholder Stakeholder @relation(fields: [stakeholderId], references: [id])
stakeholderId String
Safe Safe[]
safe Safe?
fields DocumentCustomField[]
SafeSigningToken SafeSigningToken[]

@@index([stakeholderId])
}

model SafeSigningToken {
id String @id @default(cuid())
status SafeSigningStatus @default(PENDING)
token String @unique
id String @id @default(cuid())
token String @unique

signerMember SafeSignerMember? @relation(fields: [signerMemberId], references: [id])
signerMemberId String?
Expand Down
34 changes: 34 additions & 0 deletions src/app/(documents)/safe/[token]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { SafeSigningForm } from "@/components/safe/safe-signing-form";
import { SafeTemplateRenderer } from "@/components/safe/safe-template-render";
import { SigningFieldRenderer } from "@/components/signing-field/signing-field-renderer";
import { Button } from "@/components/ui/button";
import { SafeSigningFieldProvider } from "@/providers/safe-signing-field-provider";
import { api } from "@/trpc/server";

export default async function SafeSignPage({
params,
}: { params: { token: string } }) {
const fields = await api.safe.getSigningFields.query({ token: params.token });

return (
<SafeSigningFieldProvider safeId={fields.safeId} token={params.token}>
<div className="flex min-h-screen bg-gray-50">
<div className="flex h-full flex-grow flex-col">
<div className="mx-auto min-h-full w-full px-5 py-10 lg:px-8 2xl:max-w-screen-xl">
<div className="grid grid-cols-12">
<div className="col-span-12 ">
<SafeTemplateRenderer />
</div>
</div>
</div>
</div>
<div className="sticky top-0 flex min-h-full w-80 flex-col lg:border-l">
<SafeSigningForm>
<SigningFieldRenderer fields={fields.fields} />
<Button type="submit">Complete signing</Button>
</SafeSigningForm>
</div>
</div>
</SafeSigningFieldProvider>
);
}
47 changes: 33 additions & 14 deletions src/components/safe/form/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"use client";

import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
Expand All @@ -24,16 +25,17 @@ import { z } from "zod";
import { PrePostSelector } from "./pre-post-selector";

// Safe document preview
import { PostMoneyCap, PostMoneyDiscount } from "@/components/safe/templates";
import { PostMoneyDiscount } from "@/components/safe/templates";
import { createSafe } from "@/server/api/client-handlers/safe";
import { PDFViewer } from "@react-pdf/renderer";
import { useMutation } from "@tanstack/react-query";
import { useSession } from "next-auth/react";

type SafeFormProps = {
type: "create" | "import";
};

const SafeFormSchema = z.object({
id: z.string().optional(),
publicId: z.string().optional(),
type: z.nativeEnum(SafeTypeEnum),
status: z.nativeEnum(SafeStatusEnum),
capital: z.coerce.number(),
Expand All @@ -42,14 +44,19 @@ const SafeFormSchema = z.object({
mfn: z.boolean().optional().default(false),
proRata: z.boolean().optional().default(false),
issueDate: z.string().date(),
stakeholderId: z.string(),
memberId: z.string(),
signerStakeholderId: z.string(),
signerMemberId: z.string(),
bankAccountId: z.string(),
});

type SafeFormType = z.infer<typeof SafeFormSchema>;

const placeholder = "________________________________________";

export const SafeForm: React.FC<SafeFormProps> = ({ type }) => {
const { data: session } = useSession();
const { mutate } = useMutation(createSafe);

const form = useForm<SafeFormType>({
resolver: zodResolver(SafeFormSchema),
defaultValues: {
Expand All @@ -64,7 +71,10 @@ export const SafeForm: React.FC<SafeFormProps> = ({ type }) => {
const isSubmitting = form.formState.isSubmitting;

const handleSubmit = (data: SafeFormType) => {
console.log(data);
mutate({
json: { ...data },
urlParams: { companyId: session?.user.companyId ?? "" },
});
};

return (
Expand All @@ -83,7 +93,7 @@ export const SafeForm: React.FC<SafeFormProps> = ({ type }) => {
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-5 mt-5">
<FormField
control={form.control}
name="stakeholderId"
name="signerMemberId"
render={() => {
return (
<FormItem>
Expand All @@ -96,7 +106,7 @@ export const SafeForm: React.FC<SafeFormProps> = ({ type }) => {
</FormLabel>
<MemberSelector
onSelect={(value: string) => {
form.setValue("memberId", value);
form.setValue("signerMemberId", value);
}}
/>

Expand All @@ -108,14 +118,14 @@ export const SafeForm: React.FC<SafeFormProps> = ({ type }) => {

<FormField
control={form.control}
name="stakeholderId"
name="signerStakeholderId"
render={() => {
return (
<FormItem>
<FormLabel>Investor</FormLabel>
<InvestorSelector
onSelect={(value: string) => {
form.setValue("stakeholderId", value);
form.setValue("signerStakeholderId", value);
}}
/>
<FormMessage className="text-xs font-light" />
Expand All @@ -126,7 +136,7 @@ export const SafeForm: React.FC<SafeFormProps> = ({ type }) => {

<FormField
control={form.control}
name="stakeholderId"
name="bankAccountId"
render={() => {
return (
<FormItem>
Expand Down Expand Up @@ -336,8 +346,17 @@ export const SafeForm: React.FC<SafeFormProps> = ({ type }) => {
keywords: "YC, SAFE, Post Money, Discount",
}}
investor={{
name: "Puru Dahal",
email: "",
name: placeholder,
email: placeholder,
address: placeholder,
signature: placeholder,
title: placeholder,
}}
sender={{
email: placeholder,
name: placeholder,
title: placeholder,
signature: placeholder,
}}
investment={100000}
valuation={1000000}
Expand Down
40 changes: 40 additions & 0 deletions src/components/safe/safe-signing-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"use client";

import type { SafeSigningFieldForm } from "@/providers/safe-signing-field-provider";
import { api } from "@/trpc/react";
import { useRouter } from "next/navigation";
import type { ReactNode } from "react";
import { useFormContext } from "react-hook-form";
import { toast } from "sonner";

interface SafeSigningFormProps {
children: ReactNode;
}

export function SafeSigningForm({ children }: SafeSigningFormProps) {
const { handleSubmit } = useFormContext<SafeSigningFieldForm>();
const router = useRouter();
const { mutate } = api.safe.sign.useMutation({
onSuccess() {
toast.success("🎉 Great job, you are done signing this document.");
router.push("/");
},
});

const onSubmit = (values: SafeSigningFieldForm) => {
mutate({
data: values.fieldValues,
safeId: values.safeId,
token: values.token,
});
};

return (
<form
onSubmit={handleSubmit(onSubmit)}
className="flex flex-col gap-y-4 px-2 py-10"
>
{children}
</form>
);
}
35 changes: 35 additions & 0 deletions src/components/safe/safe-template-render.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"use client";

import { PDFViewer } from "@react-pdf/renderer";
import { PostMoneyDiscount } from "./templates";

export function SafeTemplateRenderer() {
return (
<PDFViewer
className="w-full h-screen border-none rounded-md"
showToolbar={false}
>
<PostMoneyDiscount
options={{
author: "Y Combinator",
creator: "Captable, Inc.",
producer: "Captable, Inc.",
title: "YC SAFE - Post Money Discount",
subject: "YC SAFE - Post Money Discount",
keywords: "YC, SAFE, Post Money, Discount",
}}
investor={{
name: "Puru Dahal",
email: "",
}}
investment={100000}
discountRate={10}
date={new Date().toISOString()}
company={{
name: "Company Inc.",
state: "CA",
}}
/>
</PDFViewer>
);
}
5 changes: 4 additions & 1 deletion src/components/safe/templates/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,15 @@ export type SafeProps = {
name: string;
email: string;
title: string;
signature?: string;
};

investor: {
name: string;
email: string;
address?: string;
address?: string | null;
title?: string;
signature?: string;
};

options: {
Expand Down
Loading