Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 12 additions & 11 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ model Stakeholder {
city String?
state String?
zipcode String?
country String @default("US")
country String @default("US")

companyId String
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
Expand Down Expand Up @@ -564,26 +564,27 @@ enum SafeStatusEnum {
EXPIRED
CANCELLED
}

// YC Standard Safe
enum SafeTemplateEnum {
POST_MONEY_CAP @map("Valuation Cap, no Discount")
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_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_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")
POST_MONEY_MFN_WITH_PRO_RATA @map("MFN, no Valuation Cap, no Discount, include Pro Rata Rights")

CUSTOM @map("Custom")
}

model Safe {
id String @id @default(cuid())
publicId String // eg. SAFE-01
type SafeTypeEnum @default(POST_MONEY)
status SafeStatusEnum @default(DRAFT)
capital Float // Amount of money invested
safeTemplate SafeTemplateEnum?
id String @id @default(cuid())
publicId String // eg. SAFE-01
type SafeTypeEnum @default(POST_MONEY)
status SafeStatusEnum @default(DRAFT)
capital Float // Amount of money invested
safeTemplate SafeTemplateEnum?

valuationCap Float?
discountRate Float?
Expand Down
34 changes: 24 additions & 10 deletions src/common/uploads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
getPresignedPutUrl,
type getPresignedUrlOptions,
} from "@/server/file-uploads";

import { generatePublicId } from "./id";
/**
* usage
* ```js
Expand All @@ -19,20 +19,35 @@ import {
*/

export const uploadFile = async (
file: File,
file: File | Buffer,
options: Pick<
getPresignedUrlOptions,
"expiresIn" | "keyPrefix" | "identifier"
>,
bucketMode: "publicBucket" | "privateBucket" = "privateBucket",
) => {
const isFile = file instanceof File;
let fileType: string;
let fileName: string;
let fileSize: number;

if (isFile) {
fileName = file.name;
fileType = file.type;
fileSize = file.size;
} else {
fileName = `Safe-template-${generatePublicId()}`;
fileType = "application/pdf";
fileSize = file.byteLength;
}

const { url, key, bucketUrl } = await getPresignedPutUrl({
contentType: file.type,
fileName: file.name,
contentType: fileType,
fileName,
bucketMode,
...options,
});
const body = await file.arrayBuffer();
const body = isFile ? await file.arrayBuffer() : file;
const res = await fetch(url, {
method: "PUT",
headers: {
Expand All @@ -42,11 +57,10 @@ export const uploadFile = async (
});
if (!res.ok) {
throw new Error(
`Failed to upload file "${file.name}", failed with status code ${res.status}`,
`Failed to upload file "${fileName}", failed with status code ${res.status}`,
);
}

const { name, type, size } = file;
let fileUrl = bucketUrl;

if (bucketMode === "publicBucket" && process.env.NEXT_PUBLIC_UPLOAD_DOMAIN) {
Expand All @@ -55,9 +69,9 @@ export const uploadFile = async (

return {
key,
name,
mimeType: type,
size,
name: fileName,
mimeType: fileType,
size: fileSize,
fileUrl,
};
};
Expand Down
53 changes: 40 additions & 13 deletions src/components/safe/new/modal.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useState } from "react";
import { api } from "@/trpc/react";
import { useForm } from "react-hook-form";
import useSteps from "@/components/safe/new/steps";
import { zodResolver } from "@hookform/resolvers/zod";
import MultiStepModal from "@/components/shared/multistep-modal";
import { useRouter } from "next/navigation";
import useSafeSteps from "./useSafeSteps";
import { useSession } from "next-auth/react";
import { useToast } from "@/components/ui/use-toast";
import {
type SafeMutationType,
SafeMutationSchema,
Expand All @@ -18,21 +19,47 @@ export default function CreateNewSafe({
companyId,
trigger,
}: CreateNewSafeType) {
const steps = useSteps({ companyId });
const formSchema = SafeMutationSchema;
const { mutateAsync } = api.safe.create.useMutation();

const [open, setOpen] = useState(false);
const { data: session } = useSession();
const router = useRouter();

const form = useForm<SafeMutationType>({
resolver: zodResolver(formSchema),
});
const steps = useSafeSteps({ companyId });
const formSchema = SafeMutationSchema;
const { toast } = useToast();

const isSubmitting = form.formState.isSubmitting;
const { mutateAsync } = api.safe.create.useMutation({
onSuccess: (payload) => {
console.log({ payload });
const isSuccess = payload?.success;
const message = payload?.message;
toast({
variant: isSuccess ? "default" : "destructive",
title: isSuccess
? "🎉 Successfully created SAFEs."
: "Failed creating safe",
description: message,
});
if (isSuccess) {
router.push(
`/${session?.user.companyPublicId}/templates/${payload?.template?.publicId}`,
);
}
setOpen(false);
},
});

const onSubmit = async (values: SafeMutationType) => {
// await mutateAsync({ values });
setOpen(false);
if (values.safeTemplate !== "CUSTOM") {
await mutateAsync(values);
}
if (
values.safeTemplate === "CUSTOM" &&
values?.documents?.length === 1 &&
values?.documents[0]?.bucketId &&
values?.documents[0]?.name
) {
await mutateAsync(values);
}
};

return (
Expand Down
72 changes: 0 additions & 72 deletions src/components/safe/new/steps.tsx

This file was deleted.

55 changes: 55 additions & 0 deletions src/components/safe/new/steps/documents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"use client";

import Uploader from "@/components/ui/uploader";
import React from "react";
import { useFormContext } from "react-hook-form";
import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";

type Documents = {
bucketId: string;
name: string;
};

export const DocumentsFields = ["documents"];

export const Documents = () => {
const form = useFormContext();
//eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const documents: [Documents] = form.watch("documents");
// document=uploaders are happy format then///
return (
<>
<Uploader
multiple={false}
identifier={"documenter"}
keyPrefix="equity-doc"
onSuccess={async (bucketData) => {
form.setValue("documents", [
{
bucketId: bucketData.id,
name: bucketData.name,
},
]);
}}
accept={{
"application/pdf": [".pdf"],
}}
/>
{documents?.length ? (
<Alert className="mt-5 bg-teal-100" variant="default">
<AlertTitle>Successfully uploaded the template.</AlertTitle>
<AlertDescription>
You can submit the form to proceed.
</AlertDescription>
</Alert>
) : (
<Alert variant="destructive" className="mt-5">
<AlertTitle>No template uploaded</AlertTitle>
<AlertDescription>
Please upload necessary template to continue.
</AlertDescription>
</Alert>
)}
</>
);
};
Loading