Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: safe signing #528

Open
wants to merge 69 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ await import("./src/env.js");

/** @type {import("next").NextConfig} */
const nextConfig = {
swcMinify: true,
output: process.env.DOCKER_OUTPUT ? "standalone" : undefined,
images: {
remotePatterns: [
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@
"postinstall": "prisma generate",
"prepare": "husky",
"lint": "biome check --apply ./src",
"email:dev": "mailpit",
"email:dev": "email dev --dir ./src/emails --port 3005",
"format": "biome format --write ./src && prisma format",
"email:preview": "email preview ./src/emails",
"copy:pdfjs": "node scripts/copy-pdfjs-worker.cjs",
"knip": "knip",
"test": "vitest"
Expand All @@ -34,6 +33,7 @@
"@aws-sdk/client-s3": "^3.577.0",
"@aws-sdk/s3-request-presigner": "^3.577.0",
"@blocknote/react": "^0.12.2",
"@headlessui/react": "^2.1.2",
"@hono/swagger-ui": "^0.4.0",
"@hono/zod-openapi": "^0.15.1",
"@hookform/resolvers": "^3.9.0",
Expand Down Expand Up @@ -90,7 +90,7 @@
"lodash-es": "^4.17.21",
"mime": "^4.0.3",
"nanoid": "^5.0.4",
"next": "^14.2.4",
"next": "^14.2.14",
"next-auth": "^4.24.7",
"next-nprogress-bar": "^2.3.13",
"nodemailer": "^6.9.14",
Expand Down
578 changes: 370 additions & 208 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
Warnings:

- The values [Valuation Cap, no Discount,Discount, no Valuation Cap,MFN, no Valuation Cap, no Discount,Valuation Cap, no Discount, include Pro Rata Rights,Discount, no Valuation Cap, include Pro Rata Rights,MFN, no Valuation Cap, no Discount, include Pro Rata Rights] on the enum `SafeTemplateEnum` will be removed. If these variants are still used in the database, this will fail.

*/
-- AlterEnum
BEGIN;
CREATE TYPE "SafeTemplateEnum_new" AS ENUM ('YC - Valuation Cap, no Discount', 'YC - Discount, no Valuation Cap', 'YC - MFN, no Valuation Cap, no Discount', 'YC - Valuation Cap, Pro Rata, no Discount', 'YC - Discount, Pro Rata, no Valuation Cap', 'YC - MFN, Pro Rata, no Valuation Cap & Discount', 'Custom');
ALTER TABLE "Safe" ALTER COLUMN "safeTemplate" TYPE "SafeTemplateEnum_new" USING ("safeTemplate"::text::"SafeTemplateEnum_new");
ALTER TYPE "SafeTemplateEnum" RENAME TO "SafeTemplateEnum_old";
ALTER TYPE "SafeTemplateEnum_new" RENAME TO "SafeTemplateEnum";
DROP TYPE "SafeTemplateEnum_old";
COMMIT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Safe" ALTER COLUMN "boardApprovalDate" DROP NOT NULL;
14 changes: 14 additions & 0 deletions prisma/migrations/20240717202810_missing_migration/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
Warnings:

- The values [YC - Valuation Cap, no Discount,YC - Discount, no Valuation Cap,YC - MFN, no Valuation Cap, no Discount,YC - Valuation Cap, Pro Rata, no Discount,YC - Discount, Pro Rata, no Valuation Cap,YC - MFN, Pro Rata, no Valuation Cap & Discount] on the enum `SafeTemplateEnum` will be removed. If these variants are still used in the database, this will fail.

*/
-- AlterEnum
BEGIN;
CREATE TYPE "SafeTemplateEnum_new" AS ENUM ('YC Post Money - Valuation Cap, no Discount', 'YC Post Money - Discount, no Valuation Cap', 'YC Post Money - MFN, no Valuation Cap, no Discount', 'YC Post Money - Valuation Cap, Pro Rata, no Discount', 'YC Post Money - Discount, Pro Rata, no Valuation Cap', 'YC Post Money - MFN, Pro Rata, no Valuation Cap & Discount', 'YC Pre Money - Valuation Cap, no Discount', 'YC Pre Money - Valuation Cap & Discount', 'YC Pre Money - Discount, no Valuation Cap', 'YC Pre Money - MFN, no Valuation Cap', 'Custom');
ALTER TABLE "Safe" ALTER COLUMN "safeTemplate" TYPE "SafeTemplateEnum_new" USING ("safeTemplate"::text::"SafeTemplateEnum_new");
ALTER TYPE "SafeTemplateEnum" RENAME TO "SafeTemplateEnum_old";
ALTER TYPE "SafeTemplateEnum_new" RENAME TO "SafeTemplateEnum";
DROP TYPE "SafeTemplateEnum_old";
COMMIT;
12 changes: 12 additions & 0 deletions prisma/migrations/20240905205102_missing_migration/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
Warnings:

- Added the required column `bankAccountId` to the `Safe` table without a default value. This is not possible if the table is not empty.

*/
-- AlterTable
ALTER TABLE "Safe" ADD COLUMN "bankAccountId" TEXT NOT NULL,
ALTER COLUMN "publicId" DROP NOT NULL;

-- CreateIndex
CREATE INDEX "Safe_bankAccountId_idx" ON "Safe"("bankAccountId");
103 changes: 103 additions & 0 deletions prisma/migrations/20241007174706_add_safe_fiekds/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
Warnings:

- You are about to drop the column `stakeholderId` on the `Safe` table. All the data in the column will be lost.
- A unique constraint covering the columns `[signerMemberId]` on the table `Safe` will be added. If there are existing duplicate values, this will fail.
- A unique constraint covering the columns `[signerStakeholderId]` on the table `Safe` will be added. If there are existing duplicate values, this will fail.
- Added the required column `signerMemberId` to the `Safe` table without a default value. This is not possible if the table is not empty.
- Added the required column `signerStakeholderId` to the `Safe` table without a default value. This is not possible if the table is not empty.
- Made the column `publicId` on table `Safe` required. This step will fail if there are existing NULL values in that column.

*/
-- CreateEnum
CREATE TYPE "SafeSigningStatus" AS ENUM ('SIGNED', 'PENDING');

-- DropIndex
DROP INDEX "Safe_stakeholderId_idx";

-- AlterTable
ALTER TABLE "Safe" DROP COLUMN "stakeholderId",
ADD COLUMN "signerMemberId" TEXT NOT NULL,
ADD COLUMN "signerStakeholderId" TEXT NOT NULL,
ALTER COLUMN "publicId" SET NOT NULL;

-- CreateTable
CREATE TABLE "SafeSignerMember" (
"id" TEXT NOT NULL,
"status" "SafeSigningStatus" NOT NULL DEFAULT 'PENDING',
"memberId" TEXT NOT NULL,

CONSTRAINT "SafeSignerMember_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "SafeSignerStakeholder" (
"id" TEXT NOT NULL,
"status" "SafeSigningStatus" NOT NULL DEFAULT 'PENDING',
"stakeholderId" TEXT NOT NULL,

CONSTRAINT "SafeSignerStakeholder_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "SafeSigningToken" (
"id" TEXT NOT NULL,
"token" TEXT NOT NULL,
"signerMemberId" TEXT,
"signerStakeholderId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "SafeSigningToken_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "DocumentCustomField" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"type" "FieldTypes" NOT NULL DEFAULT 'TEXT',
"defaultValue" TEXT NOT NULL DEFAULT '',
"readOnly" BOOLEAN NOT NULL DEFAULT false,
"required" BOOLEAN NOT NULL DEFAULT false,
"prefilledValue" TEXT,
"meta" JSONB NOT NULL DEFAULT '{}',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"safeSignerStakeholderId" TEXT,
"safeSignerMemberId" TEXT,

CONSTRAINT "DocumentCustomField_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE INDEX "SafeSignerMember_memberId_idx" ON "SafeSignerMember"("memberId");

-- CreateIndex
CREATE INDEX "SafeSignerStakeholder_stakeholderId_idx" ON "SafeSignerStakeholder"("stakeholderId");

-- CreateIndex
CREATE UNIQUE INDEX "SafeSigningToken_token_key" ON "SafeSigningToken"("token");

-- CreateIndex
CREATE INDEX "SafeSigningToken_signerStakeholderId_idx" ON "SafeSigningToken"("signerStakeholderId");

-- CreateIndex
CREATE INDEX "SafeSigningToken_signerMemberId_idx" ON "SafeSigningToken"("signerMemberId");

-- CreateIndex
CREATE INDEX "DocumentCustomField_safeSignerStakeholderId_idx" ON "DocumentCustomField"("safeSignerStakeholderId");

-- CreateIndex
CREATE INDEX "DocumentCustomField_safeSignerMemberId_idx" ON "DocumentCustomField"("safeSignerMemberId");

-- CreateIndex
CREATE UNIQUE INDEX "Safe_signerMemberId_key" ON "Safe"("signerMemberId");

-- CreateIndex
CREATE UNIQUE INDEX "Safe_signerStakeholderId_key" ON "Safe"("signerStakeholderId");

-- CreateIndex
CREATE INDEX "Safe_signerMemberId_idx" ON "Safe"("signerMemberId");

-- CreateIndex
CREATE INDEX "Safe_signerStakeholderId_idx" ON "Safe"("signerStakeholderId");
119 changes: 102 additions & 17 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ model BankAccount {

companyId String
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
safes Safe[]

@@unique([companyId, accountNumber])
@@unique([companyId, primary], name: "unique_primary_account")
Expand Down Expand Up @@ -232,6 +233,7 @@ model Member {
updates Update[]
dataRooms DataRoomRecipient[]
UpdateRecipient UpdateRecipient[]
SafeSignerMember SafeSignerMember[]

@@unique([companyId, userId])
@@index([companyId])
Expand Down Expand Up @@ -295,13 +297,13 @@ model Stakeholder {
investments Investment[]
shares Share[]
options Option[]
safes Safe[]
convertibleNotes ConvertibleNote[]
updates UpdateRecipient[]
dataRooms DataRoomRecipient[]

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
SafeSignerStakeholder SafeSignerStakeholder[]

@@index([companyId])
}
Expand Down Expand Up @@ -811,20 +813,24 @@ enum SafeStatusEnum {

// YC Standard Safe
enum SafeTemplateEnum {
POST_MONEY_CAP @map("Valuation Cap, no Discount")
POST_MONEY_DISCOUNT @map("Discount, no Valuation Cap")
POST_MONEY_MFN @map("MFN, no Valuation Cap, no Discount")

POST_MONEY_CAP_WITH_PRO_RATA @map("Valuation Cap, no Discount, include Pro Rata Rights")
POST_MONEY_DISCOUNT_WITH_PRO_RATA @map("Discount, no Valuation Cap, include Pro Rata Rights")
POST_MONEY_MFN_WITH_PRO_RATA @map("MFN, no Valuation Cap, no Discount, include Pro Rata Rights")
YC_POST_1 @map("YC Post Money - Valuation Cap, no Discount")
YC_POST_2 @map("YC Post Money - Discount, no Valuation Cap")
YC_POST_3 @map("YC Post Money - MFN, no Valuation Cap, no Discount")
YC_POST_4 @map("YC Post Money - Valuation Cap, Pro Rata, no Discount")
YC_POST_5 @map("YC Post Money - Discount, Pro Rata, no Valuation Cap")
YC_POST_6 @map("YC Post Money - MFN, Pro Rata, no Valuation Cap & Discount")

YC_PRE_1 @map("YC Pre Money - Valuation Cap, no Discount")
YC_PRE_3 @map("YC Pre Money - Valuation Cap & Discount")
YC_PRE_2 @map("YC Pre Money - Discount, no Valuation Cap")
YC_PRE_4 @map("YC Pre Money - MFN, no Valuation Cap")

CUSTOM @map("Custom")
}

model Safe {
id String @id @default(cuid())
publicId String // eg. SAFE-01
publicId String
type SafeTypeEnum @default(POST_MONEY)
status SafeStatusEnum @default(DRAFT)
capital Float // Amount of money invested
Expand All @@ -839,22 +845,76 @@ model Safe {

documents Document[]

stakeholderId String
stakeholder Stakeholder @relation(fields: [stakeholderId], references: [id], onDelete: Cascade)

companyId String
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)

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

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

bankAccountId String
bankAccount BankAccount @relation(fields: [bankAccountId], references: [id], onDelete: Cascade)

issueDate DateTime
boardApprovalDate DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
boardApprovalDate DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@unique([publicId, companyId])
@@index([companyId])
@@index([bankAccountId])
@@index([signerMemberId])
@@index([signerStakeholderId])
}

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

@@index([memberId])
}

enum SafeSigningStatus {
SIGNED
PENDING
}

model SafeSignerStakeholder {
id String @id @default(cuid())
status SafeSigningStatus @default(PENDING)
stakeholder Stakeholder @relation(fields: [stakeholderId], references: [id])
stakeholderId String
safe Safe?
fields DocumentCustomField[]
SafeSigningToken SafeSigningToken[]

@@index([stakeholderId])
}

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

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

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

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@index([signerStakeholderId])
@@index([signerMemberId])
}

enum ConvertibleStatusEnum {
DRAFT
ACTIVE
Expand Down Expand Up @@ -1143,3 +1203,28 @@ model AccessToken {
@@index([userId])
@@index([typeEnum, clientId])
}

model DocumentCustomField {
id String @id @default(cuid())
name String
type FieldTypes @default(TEXT)
defaultValue String @default("")
readOnly Boolean @default(false)
required Boolean @default(false)
prefilledValue String?

/// [TemplateFieldMeta]
meta Json @default("{}")

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

safeSignerStakeholder SafeSignerStakeholder? @relation(fields: [safeSignerStakeholderId], references: [id])
safeSignerStakeholderId String?

safeSignerMember SafeSignerMember? @relation(fields: [safeSignerMemberId], references: [id])
safeSignerMemberId String?

@@index([safeSignerStakeholderId])
@@index([safeSignerMemberId])
}
Binary file added public/assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const EsignDocumentPage = async () => {
subtitle="Click the button below to upload a new document for electronic signature."
>
<AddEsignDocumentButton
title="esign a Document"
title="eSign a Document"
subtitle=""
companyPublicId={session.user.companyPublicId}
/>
Expand All @@ -38,7 +38,7 @@ const EsignDocumentPage = async () => {
description="Upload, sign and send documents for electronic signatures."
action={
<AddEsignDocumentButton
title="esign a Document"
title="eSign a document"
subtitle=""
companyPublicId={session.user.companyPublicId}
/>
Expand Down
Loading