Skip to content

Commit

Permalink
base update
Browse files Browse the repository at this point in the history
itsmegood committed Jan 30, 2024
1 parent 3c941ed commit e0d5952
Showing 16 changed files with 1,085 additions and 60 deletions.
318 changes: 318 additions & 0 deletions app/components/layout/header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
import { Form, Link, useMatches, useSubmit } from '@remix-run/react'
import { useEffect, useRef, useState } from 'react'

import { getUserImgSrc } from '#app/utils/misc.tsx'
import { useOptionalUser, useUser } from '#app/utils/user.ts'
import { SearchBar } from '../search-bar.tsx'
import { Button } from '../ui/button.tsx'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuPortal,
DropdownMenuTrigger,
} from '../ui/dropdown-menu.tsx'
import { Icon } from '../ui/icon.tsx'
import { MobileNav } from './mobile-nav.tsx'

export function HeaderNav({
sticky,
mobileNavItems,
clean = false,
}: {
sticky?: boolean
mobileNavItems?: {
title: string
items: {
title: string
href: string
}[]
}[]
clean?: boolean
}) {
const user = useOptionalUser()

const [searchExpand, setSearchExpand] = useState(false)
const searchRef = useRef<HTMLInputElement>(null)

const matches = useMatches()
const isProjectPage = matches.find(
match => match.id === 'routes/projects+/index',
)

const handleSearchExpand = () => {
setSearchExpand(true)
// searchRef.current?.focus()
}

useEffect(() => {
if (searchExpand && searchRef.current) {
searchRef.current.focus()
}
}, [searchExpand])

return (
<>
<header
className={
sticky
? 'sticky top-0 z-50 rounded-sm bg-background/95 shadow-sm backdrop-blur-md'
: 'shadow-sm'
}
>
<nav className="inline-flex w-full items-center justify-between p-3">
<div className="flex items-center justify-between gap-2 md:w-1/4">
<MobileNav menuItems={mobileNavItems} />

<Link to="/" className="inline-flex items-center">
<svg
className="mr-2 cursor-none text-primary lg:cursor-auto"
width="35"
height="35"
viewBox="0 0 97 85"
fill="none"
>
<path
d="M96.1608 1.3148C98.5608 36.1148 73.4941 47.1481 60.6608 48.3148C81.8608 52.3148 84.4941 71.9815 83.1608 81.3148C57.9608 85.3148 50.9941 60.9815 50.6608 48.3148C51.8608 5.9148 81.4941 -0.685196 96.1608 1.3148Z"
fill="currentColor"
stroke="currentColor"
/>
<path
d="M1.16077 1.3148C-1.23923 36.1148 23.8274 47.1481 36.6608 48.3148C15.4608 52.3148 12.8274 71.9815 14.1608 81.3148C39.3608 85.3148 46.3274 60.9815 46.6608 48.3148C45.4608 5.9148 15.8274 -0.685196 1.16077 1.3148Z"
fill="currentColor"
stroke="currentColor"
/>
</svg>
<span className="hidden text-2xl font-semibold text-primary md:block">
BloomBacker
</span>
</Link>
</div>

{!clean && (
<>
<div
className={
searchExpand
? 'absolute left-0 top-0 z-50 inline-flex w-full items-center justify-center gap-2 border-b bg-background p-3 md:hidden'
: 'flex px-8 md:w-2/4'
}
>
{searchExpand && (
<Button size="sm" onClick={() => setSearchExpand(false)}>
<Icon name="arrow-left" size="sm" />
<span className="sr-only">Search</span>
</Button>
)}

{!isProjectPage && (
<SearchBar
action="/projects"
status="idle"
searchRef={searchRef}
onSubmitHandler={_ => {
searchRef.current?.blur()
setSearchExpand(false)
}}
className={searchExpand ? '' : 'hidden md:flex'}
/>
)}
</div>

<div className="flex items-center justify-end md:w-1/4 md:justify-between">
<div className="hidden items-center gap-4 md:inline-flex">
<Button asChild size="sm">
<Link to="/projects">
<Icon name="compass" size="sm">
<span className="hidden md:block">Discover</span>
</Icon>
</Link>
</Button>
<Button asChild variant="link">
<Link to="/studio/projects/new" className="hidden lg:flex">
Start A Project
</Link>
</Button>
</div>

<div className="inline-flex gap-4">
{!searchExpand && (
<>
<Button asChild size="sm">
<Link to="/projects" className="md:hidden">
<Icon name="compass" size="sm" />
</Link>
</Button>
{!isProjectPage && (
<Button
size="sm"
variant="outline"
onClick={handleSearchExpand}
className="md:hidden"
>
<Icon name="magnifying-glass" size="sm" />
<span className="sr-only">Search</span>
</Button>
)}
</>
)}
</div>
</div>
</>
)}

{user ? (
<UserDropdown />
) : (
<Button
asChild
variant="default"
size="sm"
className="whitespace-nowrap"
>
<Link to="/login">Log In</Link>
</Button>
)}
{/* <div className="block w-full sm:hidden">{searchBar}</div> */}
</nav>
</header>
</>
)
}

function UserDropdown() {
const user = useUser()
const submit = useSubmit()
const formRef = useRef<HTMLFormElement>(null)

return (
<DropdownMenu modal={false}>
<DropdownMenuTrigger asChild>
<div
// this is for progressive enhancement
onClick={e => e.preventDefault()}
className="relative rounded-full border border-primary bg-secondary p-1"
>
<div className="absolute right-0 top-0 rounded-full bg-background p-[1px]">
<div className="z-10 h-2 w-2 rounded-full bg-emerald-500"></div>
</div>
<img
className="h-7 w-7 rounded-full object-cover"
alt={user.name ?? user.username}
src={getUserImgSrc(user.image?.id)}
/>
{/* <span className="font-bold text-body-sm">
{user.name ?? user.username}
</span> */}
</div>
</DropdownMenuTrigger>
<DropdownMenuPortal>
<DropdownMenuContent
sideOffset={22}
align="end"
className="min-w-[20rem] rounded-sm border border-primary"
>
<DropdownMenuItem asChild className="border-b p-3">
<Link
prefetch="intent"
to={`/users/${user.username}`}
className="inline-flex w-full items-center gap-2 bg-muted/30 hover:bg-accent"
>
<div className="rounded-full bg-background p-1">
<img
className="h-7 w-7 rounded-full object-cover"
alt={user.name ?? user.username}
src={getUserImgSrc(user.image?.id)}
/>
</div>
<div>
<span className="text-lg">{user.username}</span>
<div className="text-xs">{user.name}</div>
</div>
</Link>
</DropdownMenuItem>
<div className="border-b p-2">
<DropdownMenuItem asChild className="p-2">
<div className="text-xs font-bold">BACKER</div>
</DropdownMenuItem>
<DropdownMenuItem asChild className="p-2">
<Link prefetch="intent" to="/" className="hover:bg-muted/30">
Home
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild className="p-2">
<Link
prefetch="intent"
to="/projects"
className="hover:bg-muted/30"
>
Discover
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild className="p-2">
<Link
prefetch="intent"
to="/studio/backed-projects"
className="hover:bg-muted/30"
>
Backed Projects
</Link>
</DropdownMenuItem>
</div>
<div className="border-b p-2">
<DropdownMenuItem asChild className="p-2">
<div className="text-xs font-bold">CREATOR</div>
</DropdownMenuItem>
<DropdownMenuItem asChild className="p-2">
<Link
prefetch="intent"
to="/studio"
className="hover:bg-muted/30"
>
Studio
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild className="p-2">
<Link
prefetch="intent"
to="/studio/projects"
className="hover:bg-muted/30"
>
Created Projects
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild className="p-2">
<Link
prefetch="intent"
to="/studio/projects/new"
className="hover:bg-muted/30"
>
Start a New Project
</Link>
</DropdownMenuItem>
</div>
<DropdownMenuItem
asChild
// this prevents the menu from closing before the form submission is completed
onSelect={event => {
event.preventDefault()
submit(formRef.current)
}}
className="cursor-pointer p-4 text-sm"
>
<Form
action="/logout"
method="POST"
ref={formRef}
className="bg-muted/30"
>
<Icon name="exit">
<button>Sign Out</button>
</Icon>
</Form>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenuPortal>
</DropdownMenu>
)
}
77 changes: 77 additions & 0 deletions app/components/layout/mobile-nav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Link } from '@remix-run/react'
import { Icon } from '../ui/icon.tsx'
import { Sheet, SheetContent, SheetTrigger } from '../ui/sheet.tsx'

export function MobileNav({
menuItems,
}: {
menuItems?: {
title: string
items: {
title: string
href: string
}[]
}[]
}) {
return (
<Sheet>
<SheetTrigger className="flex cursor-none items-center rounded-md border border-primary p-1 md:hidden">
<Icon name="menu" size="lg" />
<span className="sr-only">Toggle Menu</span>
</SheetTrigger>
<SheetContent
side="left"
className="h-screen overflow-y-auto border md:hidden"
>
<div className="flex flex-col gap-4">
<div className="inline-flex">
<svg
className="mr-2 text-primary"
width="30"
height="30"
viewBox="0 0 97 85"
fill="none"
>
<path
d="M96.1608 1.3148C98.5608 36.1148 73.4941 47.1481 60.6608 48.3148C81.8608 52.3148 84.4941 71.9815 83.1608 81.3148C57.9608 85.3148 50.9941 60.9815 50.6608 48.3148C51.8608 5.9148 81.4941 -0.685196 96.1608 1.3148Z"
fill="currentColor"
stroke="currentColor"
/>
<path
d="M1.16077 1.3148C-1.23923 36.1148 23.8274 47.1481 36.6608 48.3148C15.4608 52.3148 12.8274 71.9815 14.1608 81.3148C39.3608 85.3148 46.3274 60.9815 46.6608 48.3148C45.4608 5.9148 15.8274 -0.685196 1.16077 1.3148Z"
fill="currentColor"
stroke="currentColor"
/>
</svg>
<span className="text-2xl font-black text-primary">
BloomBacker
</span>
</div>

<ul className="divide-y">
{menuItems?.map(item => (
<li key={item.title} className="py-4">
<div className="px-2 text-xl font-bold text-primary">
{item.title}
</div>

<ul>
{item.items.map(subItem => (
<li key={subItem.title}>
<Link
to={subItem.href}
className="block cursor-none rounded-md p-2 transition hover:text-primary hover:underline"
>
{subItem.title}
</Link>
</li>
))}
</ul>
</li>
))}
</ul>
</div>
</SheetContent>
</Sheet>
)
}
51 changes: 32 additions & 19 deletions app/components/search-bar.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
import { Form, useSearchParams, useSubmit } from '@remix-run/react'
import { useId } from 'react'
import { useDebounce, useIsPending } from '#app/utils/misc.tsx'
import { cn, useDebounce, useIsPending } from '#app/utils/misc.tsx'
import { Icon } from './ui/icon.tsx'
import { Input } from './ui/input.tsx'
import { Label } from './ui/label.tsx'
import { StatusButton } from './ui/status-button.tsx'

export function SearchBar({
action,
status,
autoFocus = false,
autoFocusSearch = false,
autoSubmit = false,
onSubmitHandler,
searchRef,
className,
}: {
action: string
status: 'idle' | 'pending' | 'success' | 'error'
autoFocus?: boolean
autoFocusSearch?: boolean
onSubmitHandler?: (e: React.FormEvent<HTMLFormElement>) => void
autoSubmit?: boolean
searchRef?: React.RefObject<HTMLInputElement>
className?: string
}) {
const id = useId()
const [searchParams] = useSearchParams()
let defaultValue = searchParams.get('search') || ''

const submit = useSubmit()
const isSubmitting = useIsPending({
formMethod: 'GET',
formAction: '/users',
formAction: action,
})

const handleFormChange = useDebounce((form: HTMLFormElement) => {
@@ -30,34 +40,37 @@ export function SearchBar({
return (
<Form
method="GET"
action="/users"
className="flex flex-wrap items-center justify-center gap-2"
action={action}
className={cn(
'flex w-full flex-wrap items-center justify-center gap-2',
className,
)}
onChange={e => autoSubmit && handleFormChange(e.currentTarget)}
onSubmit={onSubmitHandler}
>
<div className="flex-1">
<div className="flex-1" key={defaultValue}>
<Label htmlFor={id} className="sr-only">
Search
</Label>
<Input
ref={searchRef}
type="search"
name="search"
id={id}
defaultValue={searchParams.get('search') ?? ''}
defaultValue={defaultValue}
placeholder="Search"
className="w-full"
autoFocus={autoFocus}
// autoFocus={autoFocusSearch}
/>
</div>
<div>
<StatusButton
type="submit"
status={isSubmitting ? 'pending' : status}
className="flex w-full items-center justify-center"
>
<Icon name="magnifying-glass" size="md" />
<span className="sr-only">Search</span>
</StatusButton>
</div>
<StatusButton
type="submit"
status={isSubmitting ? 'pending' : status}
size="sm"
>
<Icon name="magnifying-glass" size="sm" />
<span className="sr-only">Search</span>
</StatusButton>
</Form>
)
}
36 changes: 36 additions & 0 deletions app/components/ui/icons/name.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// This file is generated by npm run build:icons

export type IconName =
| 'arrow-left'
| 'arrow-right'
| 'avatar'
| 'camera'
| 'check'
| 'clock'
| 'compass'
| 'cross-1'
| 'dots-horizontal'
| 'double-arrow-left'
| 'double-arrow-right'
| 'download'
| 'envelope-closed'
| 'exit'
| 'eye-open'
| 'file-text'
| 'gift'
| 'github-logo'
| 'laptop'
| 'link-2'
| 'lock-closed'
| 'lock-open-1'
| 'magnifying-glass'
| 'menu'
| 'moon'
| 'pencil-1'
| 'pencil-2'
| 'plus'
| 'question-mark-circled'
| 'reset'
| 'sun'
| 'trash'
| 'update'
232 changes: 232 additions & 0 deletions app/components/ui/icons/sprite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
138 changes: 138 additions & 0 deletions app/components/ui/sheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import * as React from "react"
import * as SheetPrimitive from "@radix-ui/react-dialog"
import { cva, type VariantProps } from "class-variance-authority"
import { X } from "lucide-react"

import { cn } from "#app/utils/misc.tsx"

const Sheet = SheetPrimitive.Root

const SheetTrigger = SheetPrimitive.Trigger

const SheetClose = SheetPrimitive.Close

const SheetPortal = SheetPrimitive.Portal

const SheetOverlay = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
ref={ref}
/>
))
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName

const sheetVariants = cva(
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
{
variants: {
side: {
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
bottom:
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
right:
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
},
},
defaultVariants: {
side: "right",
},
}
)

interface SheetContentProps
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
VariantProps<typeof sheetVariants> {}

const SheetContent = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Content>,
SheetContentProps
>(({ side = "right", className, children, ...props }, ref) => (
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
ref={ref}
className={cn(sheetVariants({ side }), className)}
{...props}
>
{children}
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
</SheetPrimitive.Content>
</SheetPortal>
))
SheetContent.displayName = SheetPrimitive.Content.displayName

const SheetHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-2 text-center sm:text-left",
className
)}
{...props}
/>
)
SheetHeader.displayName = "SheetHeader"

const SheetFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...props}
/>
)
SheetFooter.displayName = "SheetFooter"

const SheetTitle = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Title
ref={ref}
className={cn("text-lg font-semibold text-foreground", className)}
{...props}
/>
))
SheetTitle.displayName = SheetPrimitive.Title.displayName

const SheetDescription = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
SheetDescription.displayName = SheetPrimitive.Description.displayName

export {
Sheet,
SheetPortal,
SheetOverlay,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription,
}
2 changes: 0 additions & 2 deletions app/root.tsx
Original file line number Diff line number Diff line change
@@ -230,8 +230,6 @@ function App() {
m => m.id === 'routes/c.$companyId+/_layout_company',
)

console.log('matches', matches)

const isOnSearchPage = matches.find(m => m.id === 'routes/users+/index')
const searchBar = isOnSearchPage ? null : <SearchBar status="idle" />
useToast(data.toast)
19 changes: 19 additions & 0 deletions other/svg-icons/compass.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions other/svg-icons/double-arrow-left.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions other/svg-icons/double-arrow-right.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions other/svg-icons/eye-open.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions other/svg-icons/gift.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions other/svg-icons/menu.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
109 changes: 73 additions & 36 deletions package-lock.json
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -46,8 +46,9 @@
"@epic-web/totp": "^1.1.1",
"@nasa-gcn/remix-seo": "^2.0.0",
"@paralleldrive/cuid2": "^2.2.2",
"@prisma/client": "^5.8.1",
"@prisma/client": "^5.9.0",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-slot": "^1.0.2",
@@ -87,7 +88,7 @@
"litefs-js": "^1.1.2",
"lru-cache": "^10.2.0",
"morgan": "^1.10.0",
"prisma": "^5.8.1",
"prisma": "^5.9.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -113,7 +114,7 @@
"@remix-run/serve": "^2.5.1",
"@remix-run/testing": "^2.5.1",
"@sly-cli/sly": "^1.8.0",
"@testing-library/jest-dom": "^6.3.0",
"@testing-library/jest-dom": "^6.4.0",
"@testing-library/react": "^14.1.2",
"@testing-library/user-event": "^14.5.2",
"@total-typescript/ts-reset": "^0.5.1",
89 changes: 89 additions & 0 deletions prisma/migrations/20240130113044_/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
-- CreateTable
CREATE TABLE "Company" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);

-- CreateTable
CREATE TABLE "UserCompany" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"userId" TEXT NOT NULL,
"companyId" TEXT NOT NULL,
"isOwner" BOOLEAN NOT NULL DEFAULT false,
CONSTRAINT "UserCompany_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "UserCompany_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "Company" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);

-- CreateTable
CREATE TABLE "Account" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"companyId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"balance" REAL NOT NULL,
"type" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "Account_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "Company" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);

-- CreateTable
CREATE TABLE "Transaction" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"companyId" TEXT NOT NULL,
"date" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"accountId" INTEGER NOT NULL,
"description" TEXT NOT NULL,
"amount" REAL NOT NULL,
"type" TEXT NOT NULL,
"category" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"userId" TEXT,
CONSTRAINT "Transaction_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "Company" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "Transaction_accountId_fkey" FOREIGN KEY ("accountId") REFERENCES "Account" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "Transaction_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE
);

-- CreateTable
CREATE TABLE "Invoice" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"companyId" TEXT NOT NULL,
"dateIssued" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"dueDate" DATETIME NOT NULL,
"totalAmount" REAL NOT NULL,
"status" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"userId" TEXT,
CONSTRAINT "Invoice_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "Company" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "Invoice_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE
);

-- CreateTable
CREATE TABLE "InvoiceItem" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"invoiceId" INTEGER NOT NULL,
"description" TEXT NOT NULL,
"quantity" INTEGER NOT NULL,
"price" REAL NOT NULL,
"total" REAL NOT NULL DEFAULT 0,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "InvoiceItem_invoiceId_fkey" FOREIGN KEY ("invoiceId") REFERENCES "Invoice" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);

-- CreateTable
CREATE TABLE "_RoleToUserCompany" (
"A" TEXT NOT NULL,
"B" INTEGER NOT NULL,
CONSTRAINT "_RoleToUserCompany_A_fkey" FOREIGN KEY ("A") REFERENCES "Role" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "_RoleToUserCompany_B_fkey" FOREIGN KEY ("B") REFERENCES "UserCompany" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);

-- CreateIndex
CREATE UNIQUE INDEX "_RoleToUserCompany_AB_unique" ON "_RoleToUserCompany"("A", "B");

-- CreateIndex
CREATE INDEX "_RoleToUserCompany_B_index" ON "_RoleToUserCompany"("B");

0 comments on commit e0d5952

Please sign in to comment.