Giter Site home page Giter Site logo

captableinc / captable Goto Github PK

View Code? Open in Web Editor NEW
387.0 5.0 58.0 9.99 MB

#1 Open-Source Captable, an alternative to Carta, Pully, Angelist and others.

Home Page: https://captable.inc

License: GNU Affero General Public License v3.0

JavaScript 0.74% TypeScript 97.85% CSS 1.19% Shell 0.01% Dockerfile 0.20%
captable dataroom esign fundraising investor nextjs open-source prisma typescript

captable's Introduction

Captable, Inc. cover image

Captable, Inc.

#1 Open-source Cap table management platform, an alternative to Carta, Pulley, Angelist and others.

Learn more »

Github X (formerly Twitter) Follow Join us on Discord CI License Docker image

Table of contents

✨ Key features

Important

We envision a world where cap table management is accessible, secure, and empowering for all. Captable, Inc. aims to democratize the handling of cap tables, securities, and stakeholder interactions. Through cutting-edge technology and a commitment to openness, we strive to be the catalyst for positive change in financial ecosystems.

👷 Incorporation (wip) - Captable, Inc. helps you incorporate your company in minutes, with all the necessary legal documents and filings taken care of.

👷 Cap table management (wip) - Captable, Inc. helps you keep track of your company’s ownership structure, including who owns what percentage of the company, how much stock/options has been issued, and more.

Fundraise - Captable, Inc. can help you raise capital, whether its signing standard or custom SAFE or creating and managing fundraising rounds, tracking investor commitments, and more.

Investor updates - Delight your investors and team members by sending them regular updates on your company’s progress.

eSign Documents - Sign SAFE, NDA, contracts, offere letters or any type of documents with Captable Sign.

Data rooms - Captable, Inc. provides a secure virtual data room where you can store important documents and share them with investors, employees, and other stakeholders.

🤝 Community

We have a community of developers, designers, and entrepreneurs who are passionate about building the future of finance. Join us on Discord to connect with like-minded individuals, share your ideas, and collaborate on projects.

🫡 Contributing

  • Please show us some support by giving it a ⭐️
  • We are looking for contributors to help us build the future of cap table management.
  • Let's collaborate on Discord community channel.
  • Any contributions you make are truly appreciated.

Stack


Getting started

When contributing to Captable, Inc., whether on GitHub or in other community spaces:
  • Be respectful, civil, and open-minded.
  • Before opening a new pull request, try searching through the issue tracker for known issues or fixes.

Setup development environment

Development environment on Gitpod

  • Click the button below to open this project in Gitpod.

Open in Gitpod


Development environment with Docker

  • Install Docker & Docker Compose

  • Fork & clone the forked repository

  • Install node and pnpm. (optional)

  • Copy .env.example to .env

    cp .env.example .env
  • Run the following command to start the development environment

    # With pnpm installed
    pnpm dx
    
    # Without pnpm installed
    docker compose up
    
  • Run the following command to migrate and seed the database

    docker compose exec app pnpm db:migrate
    docker compose exec app pnpm db:seed
    

    Note Everytime you make changes to Dockerfile or compose.yml, you need to rebuild the docker image by running docker compose up --build

  • Running docker compose up will start all the services on their respective ports.

    • App will be running on http://localhost:3000
    • Emails will be intercepted: http://localhost:8025
    • SMTP will be on PORT http://localhost:1025
    • Postgres will be on PORT http://localhost:5432
    • Prisma studio will be on PORT http://localhost:5555
  • Frequently used commands

    • docker compose up - Start the development environment
    • docker compose down - Stop the development environment
    • docker compose logs -f - View logs of the running services
    • docker compose up --build - Rebuild the docker image
    • docker compose run app pnpm db:migrate - Run database migrations
    • docker compose run app pnpm db:seed - Seed the database

Development environment without Docker

This has been tested on Mac OS and works really well. If you are using Linux/Windows/WSL, you might need to install some additional dependencies.

  • Fork the repository

  • Clone the repository

    git clone https://github.com/<your-github-name>/captable.git
  • Copy .env.example to .env

    cp .env.example .env
  • Install latest version of node and pnpm

  • Install latest version of postgres database

  • Install mailpit for SMTP and email interception

  • Create database captable in postgres database

  • Update .env file's DATABASE_URL with database credentials

  • For a quick start, you can use Supabase database or Neon as well.

  • To simulate file storage locally, install minio via homebrew or any other package manager.

    brew install minio

    Once minio is installed run

    minio server start --console-address ":9002"

    This will start minio server
    minio api will be available on http://127.0.0.1:9000 and
    minio web gui will be available on http://127.0.0.1:9002.

    once you see these endpoint in terminal, update the following .env:

    UPLOAD_ENDPOINT="http://127.0.0.1:9000" # should match minio api server's endpoint
    NEXT_PUBLIC_UPLOAD_DOMAIN="http://127.0.0.1:9000" # should match minio api server's endpoint
    UPLOAD_REGION="us-east-1" # don't change it
    UPLOAD_ACCESS_KEY_ID="minioadmin" # by default minio username is "minioadmin"
    UPLOAD_SECRET_ACCESS_KEY="minioadmin" # by default minio password is "minioadmin"
    UPLOAD_BUCKET_PUBLIC="captable-public-bucket"
    UPLOAD_BUCKET_PRIVATE="captable-private-bucket"

    after this, go to minio web gui(http://127.0.0.1:9002) and login:
    username: minioadmin
    password: minioadmin

    and create two buckets with the name:
    captable-public-bucket and captable-private-bucket,
    this should match UPLOAD_BUCKET_PUBLIC and UPLOAD_BUCKET_PRIVATE env's values.

    and you should be done with minio setup.

  • Run the following command to install dependencies

    pnpm install
  • Run the following command to migrate and seed the database

    pnpm db:migrate
    pnpm db:seed
  • Run the following command to start the development server

    pnpm dev
    
    # On a different terminal, run the following command to start the mail server
    pnpm email:dev
  • Frequently used commands

    • pnpm dev - Start the development server
    • pnpm email:dev - Start the mail server
    • pnpm db:migrate - Run database migrations
    • pnpm db:seed - Seed the database

Implement your changes

When making commits, make sure to follow the conventional commit guidelines, i.e. prepending the message with feat:, fix:, chore:, docs:, etc...

git add <file> && git commit -m "feat/fix/chore/docs: commit message"

Open a pull request

When you're done

Make a commit and push your code to your github fork and make a pull-request.

Thanks for your contributions. Much ❤️


💌 Contributors

A table of avatars from the project's contributors


Alt

captable's People

Contributors

aashish-upadhyay-101 avatar anikdhabal avatar aryanpandeyy avatar biomathcode avatar chetannn avatar dahal avatar dependabot[bot] avatar diwakarkashyap avatar ephraimduncan avatar g3root avatar jeremyscatigna avatar ksushant6566 avatar mehuylsharma avatar mustajab-ikram avatar raju-kadel-27 avatar rbalman avatar tedspare avatar topboyasante avatar vk-red avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

captable's Issues

Nest everything within company and store `currentCompany` => `company` object on session

OpenCap is a multi tenant application. Which means users needs to be able to toggle between companies pretty easily, when doing so:-

CleanShot 2024-01-23 at 00 51 40

  1. During initial onboarding, store company object as currentCompany on session so there is no conflict on which company's data to fetch (on server side)
  2. Replace /dashboard by /:companyId and nest all children (pages) within /:companyId.
  3. Render 404 if logged in user does not have access to the company
  4. Fetch companies (server side) to get list of companies to show on the navbar.
  5. When you toggle between companies, redirect users to respective routes: eg: opencap.co/:companyId/stakeholders
  6. When a logged in user is on /signup or /login page with currentCompany object on their session, redirect them currentCompany's page. That is: /:companyId, if they do not have currentCompany on session, redirect them to /onboarding

Reference

Implement email/password login and remove magic link

Passwordless logins are not considered secure.

  • So let's replace it with email/password.
  • Implement signup page as well and collect name and email and password
  • Send a verification email after a successful signup.
  • Send a welcome email using trigger.dev

Complete profile edit feature

We currently have a UI designed for profile page. If you navigate to [publicId]/profile you will see this UI

CleanShot 2024-02-22 at 00 55 44

We have not created trpc and hooked it up with ui to complete the feature yet. We need to implement that.

We already have file uploader helper which will help us upload file to s3 compatible bucket. Its currently being used by Uploader.tsx to upload documents. We need to implement similar feature to upload avatar image.

Please note that, these profile images will be public, so they need a different bucket. Make sure to create and use a different bucket for this and add it to the env variable PUBLIC_UPLOAD_BUCKET.

Add existing SAFEs with table and multi-step form

Issue

  • Currently Securities > Safe page is empty

Solution

  • We want to render a table with list of all current safe
  • Please use datatable to render the SAFE, it already has pagination, search and filters. (Take a look at Settings > Team for reference)
  • CTA button should be a drop down button and should say Add a SAFE
  • Drop down options are Add existing SAFE and Create a new SAFE
  • Create a multi-step form to collect all the existing SAFE information based on the schema. (we already have schema defined for safe model/table on schema.prisma
  • Steps are going to to be pretty much the same as #178 except, we are not going to ask users to pick a "SAFE template" and no Signing required, instead we should allow multiple document uploads.

CleanShot 2024-03-26 at 00 23 15

Horizontal Scroll on Landing page

Current Website as viewed on Windows, Chrome.

Issue: Horizontal Scroll appears

BrowserStack, or LambdaTest can be use to reproduce the issue.

Windows-11 (1)

Create a new SAFE with YC template and custom template

  • I have already created schema, and a multi-step modal for this feature to get started with

CleanShot 2024-03-19 at 01 53 50

  • Please implement all the following (steps are on comment) input fields and steps.
  • YC SAFEs are currently on public/yc directory.
  • If user has picked from one of these templates, remove the document step, if they have picked CUSTOM as an option on SAFE template selector, show them the document step.

If they have picked YC template

  • Upload the template to their document repository (Upload on a private bucket)
  • Redirect them to /:publicId/templates/:documentId so they can configure the template for eSign

CleanShot 2024-03-19 at 02 01 44

If they have picked CUSTOM during SAFE template selector

  • Show the documents step
  • Do not allow multiple = {false}
  • Title should be, Upload the custom SAFE template
  • After the upload, redirect them to /:publicId/templates/:documentId

Note: this PR is not going to do any esigning features/fixes.

eSign is currently being worked on by @G3root - Complete for this use case
Multi-step modal is being worked on by @Aashish-Upadhyay-101 - Complete

Render investor updates on a table

image

Render these updates on a table. Please take a look at Stakeholder's table and we need to implement this similarly. (Paginated, searchable, filterable)

Send an investor / stakeholder update

Currently we have a way to create a draft investor / stakeholder update.

CleanShot 2024-03-22 at 03 11 29

Save and continue has three options

  • Save as draft (Completed in #202)
  • Share this update (This issue #205)
  • Clone this update (Issue #206)

This issue is to implement Share this update, and here is what we need to do

  • When the Share this update option is clicked, open a modal (we have a modal component you can use)

  • Create an input with dropdown to select multiple stakeholder

  • Lets use this multi select component for this https://shadcn-extension.vercel.app/docs/multi-select

    • to support multi select (meaning, admin should be able to select multiple stakeholders)

    • Above all, on the dropdown list, lets have an additional option All stakeholders which will then select all the ones on the drop down list.

  • Use trigger.dev (already configured on our app) to send stakeholders an email with the content. Email should look similar to this UI

image

Create new company button on dashboard toggle

Something like this

CleanShot 2024-01-23 at 01 41 02

  • We do not have to make it searchable just yet.
  • We need a button to Create new company
  • When clicked, users should be redirected to /onboarding route (for now).
  • After creating the company, this issue #65 will redirect user to its respective route.

We need loading state on form submissions

Use cases are during onboarding, sending invite etc. Buttons should be disabled and we show loading wheel, shadcn has an example we can implement.

https://ui.shadcn.com/docs/components/button

import { ReloadIcon } from "@radix-ui/react-icons"
 
import { Button } from "@/components/ui/button"
 
export function ButtonLoading() {
  return (
    <Button disabled>
      <ReloadIcon className="mr-2 h-4 w-4 animate-spin" />
      Please wait
    </Button>
  )
}

CleanShot 2024-01-19 at 03 18 25

Loading state are not working for `/onboarding` and stakeholder invite buttons

Steps to reproduce

Onboarding page

  • Start the server and signup
  • It'll take you to /onboarding page
  • Complete the form and submit
  • The loading state does not work as expected.

Stakeholder invite

  • After you are redirected to dashboard, go to /stakeholder page
  • Click on + Stakeholder button
  • Complete the form and submit
  • The loading state does not work as expected.

You can find login/signup pages as reference on how its supposed to work.

Start tracking audits/events

Please note that these are going to be tracked on audit table. We already have a lib called audit for this. One additional column we need on this audit table is summary, which will have a human friendly message

Eg:
John Doe invited Will Smith as an admin
Will Smith accepted an invitation to join Company X

Here are few places we can add these events/logs to:

  • Signup
  • Company create
  • Stakeholder invite
  • Stakeholder re-invite
  • Stakeholder accept
  • Stakeholder update
  • Stakeholder remove

Add mask to inputs

We need mask on your inputs, especially number inputs. Please make sure it plays nice with react-hook-form

Eg:

  • 1000000000 => 100,000,000
  • 12345.67 => 12,345.67

Create a typesafe Audit SDK that will be used everywhere.

Reference

Lets follow the this pattern/schema ☝🏼

Example use case

Audit.create({
  "action": "user.signed_in",
  "occurred_at": "2022-08-29T19:47:52.336Z",
  "actor": {
    "type": "user",
    "id": "01GBNJC3MX9ZZJW1FSTF4C5938"
  },
  "targets": [
    {
      "type": "company",
      "id": "01GBNJD4MKHVKJGEWK42JNMBGS"
    }
  ],
  "context": {
    "location": "123.123.123.123",
    "user_agent": "Chrome/104.0.0.0"
  }
})

Create a re-usable modal with multi-step form

Create a typesafe MultiStepModal component that can be re-used anywhere. Its API could be as simple as

<MultiStepModal
  active: 1,
  title="Modal Title"
  subtitle="Modal Subtitle"
  trigger={<Button>Open Modal</Button>}
  steps={[
    {
      id: 1,
      title: "Step 1",
      component: <Step1 />
    },
    {
      id: 2,
      title: "Step 2",
      component: <Step2 />
    },
    {
      id: 3,
      title: "Step 3",
      component: <Step3 />
    }
  ]}

  submitButton={<Button>Submit</Button>}
/>

And the MultiStepModal component could be something like this

type MultiStepModalProps = {
  active: Number;
  title: String;
  subtitle?: String;
  trigger: React.ComponentType;
  steps: Array<{
    id: string,
    title: string,
    component: React.ComponentType,
  }>;
  submitButton: React.ComponentType;
};

const MultiStepModal = (
  { active, title, subtitle, trigger, steps, submitButton }: MultiStepModalProps,
) => {
  return (
    <form>
      <h1>{title}</h1>
      {subtitle && <p>{subtitle}</p>}
      {steps.map((step) => (
        <div key={step.id} style={{ display: step.id === active ? 'block' : 'none' }}>
          <step.component />

          {
            step.id !== steps[0].id && (
              <button type="button" onClick={() => setActiveStep(steps[steps.indexOf(step) - 1].id)}>
                Back
              </button>
            )
          }

          {
            step.id !== steps[steps.length - 1].id && (
              <button type="button" onClick={() => setActiveStep(steps[steps.indexOf(step) + 1].id)}>
                Next
              </button>
            )
          }

          {
            step.id === steps[steps.length - 1].id && (
              <submitButton />
            )
          }
        </div>
      ))}
    </form>
  )
};

Note: this is a just a pseudocode ☝🏼 . I am not 💯% sure how to make it work with React Hook Form, can we pass the props from parent to children components - <step.component /> in this context?

Implement stakeholder update, invite and deactivate feature.

This PR #70 is rendering stakeholders on table. @G3root had previously implemented some Reinvite and remove stakeholder feature. We will re-use some of those but what we really want to accomplish with this are as follows:-

  1. Only admin should be able to add, update, re-invite and deactivate stakeholders.
  2. admin should not be allowed to deactivate their own account (for now)
  3. Deactivate should only update membership => active status to false.

CleanShot 2024-01-25 at 22 49 08

Create securities > options page with table and multi-step form

Issue

  • Currently Securities > Options page is empty

Solution

  • We want to render a table with list of all current options (issued)
  • Create a multi-step form to collect all the options information based on the schema. (we already have schema defined for option model/table on schema.prisma

This UI for a multi-step form looks really good, let's go with similar design. Please take a look at activity-card for this design for left hand side steps

https://dribbble.com/shots/22382839-Multi-form-step

CleanShot 2024-03-05 at 00 22 02

Create a trpc backend API for file uploads.

TRPC backend API could use Cloudflare R2 for uploads for now (internally we will use that). We need to make this configurable somehow for those wanting to self host and use S3, GCP or any other platform.

@topboyasante already created an Uploader components on #28, now we just need the endpoint

Add `ip` and `userAgent` all Audits

PR #90 is keeping track of all the major events/audits/logs on major changes. Let's add ip and userAgent within Audit#context

Note: after this PR #113 is merged, we will have access to IPs on all TRPC calls. We may need another @/server/context or something similar that we can use when we are SSR on a page.tsx.

Clone an investor/stakeholder update

Currently we have a way to create a draft investor / stakeholder update.

CleanShot 2024-03-22 at 03 11 29

Save and continue has three options

  • Save as draft (Completed in #202)
  • Share this update (Issue #205)
  • Clone this update

This issue is to implement Clone this update, and here is what we need to do

  • Just create a clone of existing one, a TRPC endpoint to clone existing one, except it will be on draft and redirect users to the cloned update url

Company update endpoint

We currently have /onboarding and /company/new endpoints to create company. We currently have a setting page on this url /:publicId/company, lets implement the company edit/update feature in there.

We need pretty much everything on /company/new page, except for the Job title. One additional column we need is logo, allow users to upload company logo.

This interface looks pretty good for a settings page, we of-course need light version 😺
CleanShot 2024-02-19 at 03 56 08

Create securities > shares page with table and multi-step form

Issue

  • Currently Securities > Shares page is empty

Solution

  • We want to render a table with list of all current shares (issued)
  • Create a multi-step form to collect all the share information based on the schema. (we already have schema defined for share table.

Invite team/stakeholder

  • Find or create a user account
  • A modal with email, job title, and access (dropdown) to select their Membership#access refer to Membership model (schema) for MEMBERSHIP_ACCESS, Membership#status should be pending at this point.
  • Using next-auth, dub.sh does this really well, lets reverse engineer it. create a magic link and send an invitation email using MemberInviteEmail template.
  • They donot need onboarding, so set isOnboarded to true
  • Upon accepting invitation, update the Membership#status to accepted.
  • Create a table similar to one you see on screenshot. Table ideally should include Name, email, title, access, with three tabs on top, Active, Pending, Inactive.
  • Create an Edit action with this icon https://remixicon.com/icon/edit-line, options should be, Update, Off-board, Re-invite (if its on Pending tab)
  • Create trpc endpoints for all of the above (Update, Off-board, Re-invite) and connect them to UI.

Basically its going to be a complete feature.

Invite modal

CleanShot 2024-01-15 at 23.19.44.png

Team / Stakeholder UI

CleanShot 2024-01-15 at 23.33.48.png

Improve stakeholder invite flow

We currently add invitedEmail on Membership table when inviting new stakeholder. Instead of that

  • Find or create a user account based on the email address provided.
  • Send them login link (magic link) by creating a VerificationToken which we already do.
  • Redirect them to a verification page, where they can verify their name and email address.
  • When clicking continue, set Membership#status to accepted

Progress-bar loading spinner issue

The loading spinner is currently on the top right corner, which is not looking good, instead it would look nice to just have a progress bar without the spinner.

Screenshot 2024-03-02 at 9 37 43 PM

Create a trpc backend API for file uploads.

  • TRPC backend API could use Cloudflare R2 for uploads for now (internally we will use that). We need to make this configurable somehow for those wanting to self host and use S3, GCP or any other platform.

Update, @topboyasante already created the Uploader components on #28, now we just need the endpoint

  • This component could support both drag and drop interface and input uploads. Ideally would be nice to provide props to activate one or the other.
  • We will use different buckets for different types of uploads, so perhaps pass uploadType as prop. For example: avatar, companyLogo, incorporationDocument etc.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.