Skip to content

Commit

Permalink
Merge pull request #436 from VK-RED/feat/investor-modal
Browse files Browse the repository at this point in the history
feat: Create Investor Modal
  • Loading branch information
dahal committed Jul 17, 2024
2 parents 5f901aa + 24adea5 commit 730644f
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 20 deletions.
2 changes: 2 additions & 0 deletions src/components/modals/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { TeamMemberModal } from "./team-member/team-member-modal";
import { WipModal } from "./wip-modal";

import { AddEsignDocumentModal } from "./esign-doc";
import { InvestorModal } from "./investor/add-investor-modal";

export const { pushModal, popModal, ModalProvider } = createPushModal({
modals: {
Expand All @@ -37,5 +38,6 @@ export const { pushModal, popModal, ModalProvider } = createPushModal({
NewSafeModal,
ExistingSafeModal,
AddEsignDocumentModal,
InvestorModal,
},
});
157 changes: 157 additions & 0 deletions src/components/modals/investor/add-investor-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { popModal } from "@/components/modals";
import { Button } from "@/components/ui/button";
import { LinearCombobox } from "@/components/ui/combobox";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { camelCase } from "@/lib/utils";
import { StakeholderTypeEnum } from "@/prisma/enums";
import { api } from "@/trpc/react";
import { ZodAddStakeholderMutationSchema } from "@/trpc/routers/stakeholder-router/schema";
import { zodResolver } from "@hookform/resolvers/zod";
import { useRouter } from "next/navigation";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import type { z } from "zod";

interface InvestorFormProps {
investor?: ZodInvestorType;
}

const ZodInvestorSchema = ZodAddStakeholderMutationSchema;
type ZodInvestorType = z.infer<typeof ZodInvestorSchema>;

export const InvestorForm = ({ investor }: InvestorFormProps) => {
const router = useRouter();

const { mutateAsync: addStakeholderMutation } =
api.stakeholder.addStakeholders.useMutation({
onSuccess: ({ success, message }) => {
if (success) {
router.refresh();
form.reset();
toast.success("🎉 Investor successfully added!");
popModal();
} else {
toast.error(`🔥 Error - ${message}`);
}
},
});

const stakeholderType =
investor?.stakeholderType || investor?.institutionName
? "INSTITUTION"
: "INDIVIDUAL";

const form = useForm<ZodInvestorType>({
defaultValues: {
name: investor?.name || "",
email: investor?.email || "",
institutionName: investor?.institutionName || "",
stakeholderType,
currentRelationship: "INVESTOR",
},
resolver: zodResolver(ZodInvestorSchema),
});

const isSubmitting = form.formState.isSubmitting;

const onSubmit = (values: ZodInvestorType) => {
addStakeholderMutation([values]);
};

const StakeholderTypeArr = Object.values(StakeholderTypeEnum);

const investorTypeOpts = StakeholderTypeArr.map((st) => ({
value: st,
label: camelCase(st),
}));

return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div className="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-5 sm:grid-cols-6 md:col-span-2">
<div className="sm:col-span-3">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Full name</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage className="text-xs font-light" />
</FormItem>
)}
/>
</div>
<div className="sm:col-span-3">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage className="text-xs font-light" />
</FormItem>
)}
/>
</div>
<div className="sm:col-span-3">
<FormField
control={form.control}
name="institutionName"
render={({ field }) => (
<FormItem>
<FormLabel>Institution name</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
</FormItem>
)}
/>
</div>
<div className="sm:col-span-3">
<FormField
control={form.control}
name="stakeholderType"
render={({ field }) => (
<FormItem>
<FormLabel>Type</FormLabel>
<div>
<LinearCombobox
defaultOption={{
value: field.value,
label: camelCase(field.value),
}}
options={investorTypeOpts}
onValueChange={(option) => {
field.onChange(option.value);
}}
/>
</div>
<FormMessage className="text-xs font-light" />
</FormItem>
)}
/>
</div>
</div>
<div className="mt-8 flex justify-end">
<Button disabled={isSubmitting} loading={isSubmitting} type="submit">
{isSubmitting ? "Adding an investor" : "Add an investor"}
</Button>
</div>
</form>
</Form>
);
};
17 changes: 17 additions & 0 deletions src/components/modals/investor/add-investor-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use client";

import Modal from "@/components/common/push-modal";
import { InvestorForm } from "./add-investor-form";

type InvestorType = {
title: string | React.ReactNode;
subtitle: string | React.ReactNode;
};

export const InvestorModal = ({ title, subtitle }: InvestorType) => {
return (
<Modal size="2xl" title={title} subtitle={subtitle}>
<InvestorForm />
</Modal>
);
};
36 changes: 16 additions & 20 deletions src/components/modals/stakeholder/single-stake-holder-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@ import {
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { camelCase } from "@/lib/utils";
import {
StakeholderRelationshipEnum,
StakeholderTypeEnum,
} from "@/prisma/enums";
import { api } from "@/trpc/react";
import type { RouterOutputs } from "@/trpc/shared";
import { Label } from "@radix-ui/react-select";
import { useRouter } from "next/navigation";
import { toast } from "sonner";

Expand Down Expand Up @@ -97,26 +101,18 @@ export const SingleStakeholderForm = ({
}
};

const stakeHolderTypeOpts = [
{ value: "INDIVIDUAL", label: "Individual" },
{ value: "INSTITUTION", label: "Institution" },
];
const StakeholderTypeArr = Object.values(StakeholderTypeEnum);
const StakeholerRelationshipArr = Object.values(StakeholderRelationshipEnum);

const stakeHolderTypeOpts = StakeholderTypeArr.map((type) => ({
value: type,
label: camelCase(type),
}));

const groupTypeOpts = [
{ value: "ADVISOR", label: "Advisor" },
{ value: "BOARD_MEMBER", label: "Board member" },
{ value: "CONSULTANT", label: "Consultant" },
{ value: "EMPLOYEE", label: "Employee" },
{ value: "EX_ADVISOR", label: "Ex advisor" },
{ value: "EX_CONSULTANT", label: "Ex consultant" },
{ value: "EX_EMPLOYEE", label: "Ex employee" },
{ value: "EXECUTIVE", label: "Executive" },
{ value: "FOUNDER", label: "Founder" },
{ value: "INVESTOR", label: "Investor" },
{ value: "NON_US_EMPLOYEE", label: "Non US employee" },
{ value: "OFFICER", label: "Officer" },
{ value: "OTHER", label: "Other" },
];
const groupTypeOpts = StakeholerRelationshipArr.map((type) => ({
value: type,
label: camelCase(type),
}));

return (
<Form {...form}>
Expand Down
24 changes: 24 additions & 0 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,27 @@ export function formatCurrency(value: number, currency: "USD") {
currency: currency,
}).format(value);
}

export function camelCase(value: string) {
const arr = value.split("_");
let result = "";

const n = arr.length;

for (let i = 0; i < n; i++) {
let modifiedWord = arr[i]?.toLowerCase() as string;

// Only capitalize the first letter on the first word
if (i === 0) {
const firstLetter = modifiedWord.charAt(0).toUpperCase();
const remaining = modifiedWord.slice(1);
modifiedWord = firstLetter + remaining;
result = modifiedWord;
} else {
result += "";
result += modifiedWord;
}
}

return result;
}

0 comments on commit 730644f

Please sign in to comment.