From f9cd9c40786489497bfe66950044f29b1b0bfeae Mon Sep 17 00:00:00 2001 From: John Yeo Date: Fri, 13 Jun 2025 10:58:53 +0100 Subject: [PATCH] working on manage org modal --- package-lock.json | 17 - server/package.json | 1 - server/src/db/initDrizzle.ts | 3 +- server/src/internal/api/apiRouter.ts | 6 - .../internal/customers/attach/attachRouter.ts | 4 - .../customers/handlers/handleGetCustomer.ts | 2 +- .../customers/handlers/handlePostCustomer.ts | 4 +- .../handlers/handleUpdateBalances.ts | 2 +- server/src/internal/dev/devRouter.ts | 4 +- server/src/internal/mainRouter.ts | 5 +- .../internal/orgs/handlers/handlePostOrg.ts | 17 +- server/src/internal/orgs/orgRouter.ts | 5 +- server/src/internal/orgs/orgUtils.ts | 12 +- server/src/middleware/authMiddleware.ts | 61 +- server/src/utils/auth.ts | 65 +- server/src/utils/errorUtils.ts | 8 +- server/src/utils/models/Request.ts | 9 +- server/src/utils/routerUtils.ts | 4 +- server/tsconfig.json | 2 +- shared/archives/orgModels_old.ts | 6 - shared/db/auth-schema.ts | 10 +- shared/db/schema.ts | 17 +- shared/drizzle/0008_fresh_jackpot.sql | 17 + shared/drizzle/meta/0008_snapshot.json | 3106 +++++++++++++++++ shared/drizzle/meta/_journal.json | 21 + shared/index.ts | 1 + shared/models/orgModels/frontendOrg.ts | 15 + shared/models/orgModels/orgTable.ts | 5 - vite/src/app/layout.tsx | 11 +- vite/src/components/general/AdminHover.tsx | 17 +- vite/src/components/ui/button.tsx | 4 +- vite/src/components/ui/dialog.tsx | 14 +- vite/src/components/ui/dropdown-menu.tsx | 25 +- vite/src/hooks/useOrg.tsx | 28 + vite/src/hooks/useOrgId.tsx | 46 + vite/src/hooks/useSessionClaims.tsx | 14 - vite/src/index.css | 26 + vite/src/lib/auth-client.ts | 21 +- vite/src/main.tsx | 7 +- vite/src/services/useAxiosSwr.tsx | 4 +- vite/src/utils/formatUtils/formatTextUtils.ts | 7 +- vite/src/views/DefaultView.tsx | 9 - vite/src/views/auth/SignIn.tsx | 24 +- .../add-product/NewProductDropdown.tsx | 2 + vite/src/views/main-sidebar/SidebarTop.tsx | 60 +- .../main-sidebar/components/CreateNewOrg.tsx | 104 + .../main-sidebar/components/LogOutItem.tsx | 29 + .../main-sidebar/components/OrgDropdown.tsx | 158 + vite/src/views/onboarding/OnboardingView.tsx | 36 +- .../views/onboarding/hooks/useCreateOrg.tsx | 32 +- .../onboarding/onboarding-steps/CreateOrg.tsx | 215 +- 51 files changed, 3957 insertions(+), 365 deletions(-) create mode 100644 shared/drizzle/0008_fresh_jackpot.sql create mode 100644 shared/drizzle/meta/0008_snapshot.json create mode 100644 shared/models/orgModels/frontendOrg.ts create mode 100644 vite/src/hooks/useOrg.tsx create mode 100644 vite/src/hooks/useOrgId.tsx delete mode 100644 vite/src/hooks/useSessionClaims.tsx create mode 100644 vite/src/views/main-sidebar/components/CreateNewOrg.tsx create mode 100644 vite/src/views/main-sidebar/components/LogOutItem.tsx create mode 100644 vite/src/views/main-sidebar/components/OrgDropdown.tsx diff --git a/package-lock.json b/package-lock.json index d3d89df80..d08011285 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11057,7 +11057,6 @@ "@types/cors": "^2.8.17", "@types/express": "^5.0.0", "ai": "^4.3.10", - "autumn-js": "^0.0.46", "better-auth": "^1.2.9", "body-parser": "^1.20.3", "bullmq": "^5.31.1", @@ -11109,22 +11108,6 @@ "typescript": "^5.7.3" } }, - "server/node_modules/autumn-js": { - "version": "0.0.46", - "license": "MIT", - "dependencies": { - "rou3": "^0.6.1" - }, - "peerDependencies": { - "@tanstack/react-query": "^5.76.1", - "react": "^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - } - } - }, "server/node_modules/date-fns": { "version": "4.1.0", "license": "MIT", diff --git a/server/package.json b/server/package.json index 386722b87..273ceca83 100644 --- a/server/package.json +++ b/server/package.json @@ -45,7 +45,6 @@ "@types/cors": "^2.8.17", "@types/express": "^5.0.0", "ai": "^4.3.10", - "autumn-js": "^0.0.46", "better-auth": "^1.2.9", "body-parser": "^1.20.3", "bullmq": "^5.31.1", diff --git a/server/src/db/initDrizzle.ts b/server/src/db/initDrizzle.ts index 20d53261f..be2e62bf9 100644 --- a/server/src/db/initDrizzle.ts +++ b/server/src/db/initDrizzle.ts @@ -3,12 +3,11 @@ dotenv.config(); import postgres from "postgres"; import { drizzle } from "drizzle-orm/postgres-js"; -import { authSchema, schemas as schema } from "@autumn/shared"; +import { schemas as schema } from "@autumn/shared"; export let client = postgres(process.env.DATABASE_URL!); export let db = drizzle(client, { schema }); -export let authDb = drizzle(client, { schema: authSchema }); export const initDrizzle = () => { if (!client) { diff --git a/server/src/internal/api/apiRouter.ts b/server/src/internal/api/apiRouter.ts index 05d7d6963..e26040bcf 100644 --- a/server/src/internal/api/apiRouter.ts +++ b/server/src/internal/api/apiRouter.ts @@ -27,12 +27,6 @@ apiRouter.use(apiAuthMiddleware); apiRouter.use(pricingMiddleware); apiRouter.use(analyticsMiddleware); -apiRouter.get("/auth", (req: any, res) => { - res.json({ - message: `Authenticated -- Hello ${req.minOrg?.slug}!`, - }); -}); - apiRouter.use("/customers", cusRouter); apiRouter.use("/invoices", invoiceRouter); apiRouter.use("/products", productApiRouter); diff --git a/server/src/internal/customers/attach/attachRouter.ts b/server/src/internal/customers/attach/attachRouter.ts index 5481f7a57..5c90aa2a8 100644 --- a/server/src/internal/customers/attach/attachRouter.ts +++ b/server/src/internal/customers/attach/attachRouter.ts @@ -257,10 +257,6 @@ const handleAttachOld = async (req: any, res: any) => } logger.info("--------------------------------"); - let publicStr = req.isPublic ? "(Public) " : ""; - logger.info( - `${publicStr}ATTACH PRODUCT REQUEST (from ${req.minOrg.slug})`, - ); const { customer, diff --git a/server/src/internal/customers/handlers/handleGetCustomer.ts b/server/src/internal/customers/handlers/handleGetCustomer.ts index b5e434551..adca2a67e 100644 --- a/server/src/internal/customers/handlers/handleGetCustomer.ts +++ b/server/src/internal/customers/handlers/handleGetCustomer.ts @@ -40,7 +40,7 @@ export const handleGetCustomer = async (req: any, res: any) => if (!customer) { req.logtail.warn( - `GET /customers/${customerId}: not found | Org: ${req.minOrg.slug}`, + `GET /customers/${customerId}: not found | Org: ${org.slug}`, ); res.status(StatusCodes.NOT_FOUND).json({ message: `Customer ${customerId} not found`, diff --git a/server/src/internal/customers/handlers/handlePostCustomer.ts b/server/src/internal/customers/handlers/handlePostCustomer.ts index af502ecba..6b12fd1c2 100644 --- a/server/src/internal/customers/handlers/handlePostCustomer.ts +++ b/server/src/internal/customers/handlers/handlePostCustomer.ts @@ -56,9 +56,7 @@ export const handlePostCustomerRequest = async (req: any, res: any) => { error instanceof RecaseError && error.code === ErrCode.DuplicateCustomerId ) { - logger.warn( - `POST /customers: ${error.message} (org: ${req.minOrg.slug})`, - ); + logger.warn(`POST /customers: ${error.message} (org: ${req.org?.slug})`); res.status(error.statusCode).json({ message: error.message, code: error.code, diff --git a/server/src/internal/customers/handlers/handleUpdateBalances.ts b/server/src/internal/customers/handlers/handleUpdateBalances.ts index 5fb45d538..3e8f7122e 100644 --- a/server/src/internal/customers/handlers/handleUpdateBalances.ts +++ b/server/src/internal/customers/handlers/handleUpdateBalances.ts @@ -82,7 +82,7 @@ export const handleUpdateBalances = async (req: any, res: any) => { logger.info("--------------------------------"); logger.info( - `REQUEST: UPDATE BALANCES FOR CUSTOMER ${customer.id}, ORG: ${req.minOrg.slug}`, + `REQUEST: UPDATE BALANCES FOR CUSTOMER ${customer.id}, ORG: ${org.slug}`, ); logger.info( `Features to update: ${balances.map( diff --git a/server/src/internal/dev/devRouter.ts b/server/src/internal/dev/devRouter.ts index e7fb9844a..fd70cf248 100644 --- a/server/src/internal/dev/devRouter.ts +++ b/server/src/internal/dev/devRouter.ts @@ -57,9 +57,7 @@ devRouter.post("/api_key", withOrgAuth, async (req: any, res) => name, orgId, prefix, - meta: { - org_slug: req.minOrg.slug, - }, + meta: {}, }); res.status(200).json({ diff --git a/server/src/internal/mainRouter.ts b/server/src/internal/mainRouter.ts index c5ddc4889..c6dab96cc 100644 --- a/server/src/internal/mainRouter.ts +++ b/server/src/internal/mainRouter.ts @@ -53,11 +53,12 @@ mainRouter.use( autumnHandler({ autumn: (req: any) => { - let bearerToken = parseAuthHeader(req); + // let bearerToken = parseAuthHeader(req); return new Autumn({ - secretKey: bearerToken, + // secretKey: bearerToken, url: "http://localhost:8080/v1", + headers: req.headers, }) as any; }, identify: async (req: any) => { diff --git a/server/src/internal/orgs/handlers/handlePostOrg.ts b/server/src/internal/orgs/handlers/handlePostOrg.ts index 04381957b..7b027127c 100644 --- a/server/src/internal/orgs/handlers/handlePostOrg.ts +++ b/server/src/internal/orgs/handlers/handlePostOrg.ts @@ -15,10 +15,9 @@ export const handlePostOrg = async (req: any, res: any) => console.log("userId", userId); - const userMemberships = await auth.api.({ - userId: userId!, - }); - + // const userMemberships = await auth.api.({ + // userId: userId!, + // }); // const clerk = createClerkCli(); // const user = await clerk.users.getUser(userId!); @@ -41,11 +40,11 @@ export const handlePostOrg = async (req: any, res: any) => // role: "org:admin", // }); - // await saveOrgToDB({ - // db, - // id: org.id, - // slug: org.slug, - // }); + // await saveOrgToDB({ + // db, + // id: org.id, + // slug: org.slug, + // }); // console.log(`Created new org: ${org.id} (${org.slug})`); // } else { diff --git a/server/src/internal/orgs/orgRouter.ts b/server/src/internal/orgs/orgRouter.ts index 1da8c9f79..55925dbea 100644 --- a/server/src/internal/orgs/orgRouter.ts +++ b/server/src/internal/orgs/orgRouter.ts @@ -16,6 +16,7 @@ import { createStripeCli } from "@/external/stripe/utils.js"; import { AppEnv } from "@autumn/shared"; import { nullish } from "@/utils/genUtils.js"; import { clearOrgCache } from "./orgUtils/clearOrgCache.js"; +import { createOrgResponse } from "./orgUtils.js"; export const orgRouter = express.Router(); @@ -30,9 +31,7 @@ orgRouter.get("", async (req: any, res) => { const org = await OrgService.getFromReq(req); - res.status(200).json({ - org, - }); + res.status(200).json(createOrgResponse(org)); } catch (error) { handleRequestError({ req, diff --git a/server/src/internal/orgs/orgUtils.ts b/server/src/internal/orgs/orgUtils.ts index 4516f1ace..9bc6f49fe 100644 --- a/server/src/internal/orgs/orgUtils.ts +++ b/server/src/internal/orgs/orgUtils.ts @@ -1,6 +1,6 @@ import { decryptData, generatePublishableKey } from "@/utils/encryptUtils.js"; import RecaseError from "@/utils/errorUtils.js"; -import { AppEnv, ErrCode, Organization } from "@autumn/shared"; +import { AppEnv, ErrCode, FrontendOrg, Organization } from "@autumn/shared"; import { createSvixApp } from "@/external/svix/svixUtils.js"; import { createStripeCli } from "@/external/stripe/utils.js"; import { OrgService } from "./OrgService.js"; @@ -104,13 +104,15 @@ export const initDefaultConfig = () => { }; }; -export const createOrgResponse = (org: Organization) => { +export const createOrgResponse = (org: Organization): FrontendOrg => { return { id: org.id, + name: org.name, + logo: org.logo, slug: org.slug, - default_currency: org.default_currency, - stripe_connected: org.stripe_connected, - created_at: org.created_at, + default_currency: org.default_currency || "usd", + stripe_connected: org.stripe_connected || false, + created_at: new Date(org.createdAt).getTime(), test_pkey: org.test_pkey, live_pkey: org.live_pkey, }; diff --git a/server/src/middleware/authMiddleware.ts b/server/src/middleware/authMiddleware.ts index f4873e69b..51e75b3cd 100644 --- a/server/src/middleware/authMiddleware.ts +++ b/server/src/middleware/authMiddleware.ts @@ -1,6 +1,6 @@ import { OrgService } from "@/internal/orgs/OrgService.js"; import { auth } from "@/utils/auth.js"; -import { AuthType } from "@autumn/shared"; +import { AuthType, ErrCode } from "@autumn/shared"; import { verifyToken } from "@clerk/express"; import { fromNodeHeaders } from "better-auth/node"; import { NextFunction } from "express"; @@ -35,58 +35,57 @@ const getTokenData = async (req: any, res: any) => { }; export const withOrgAuth = async (req: any, res: any, next: NextFunction) => { + const { logtail: logger } = req; + try { - const { logtail: logger } = req; // let tokenData = await getTokenData(req, res); const session = await auth.api.getSession({ headers: fromNodeHeaders(req.headers), }); if (!session) { - logger.info(`Unauthorized - no session found`); + logger.info(`Unauthorized - no session found (${req.originalUrl})`); return res .status(401) .json({ message: "Unauthorized - no session found" }); } - throw new Error("test"); + const orgId = session?.session?.activeOrganizationId; - // if (!tokenData?.org_id) { - // throw new Error("token data has no org_id"); - // } + if (!orgId) { + logger.info(`Unauthorized - no org id found`); + return res + .status(401) + .json({ message: "Unauthorized - no org id found" }); + } // let tokenOrg = tokenData!.org as any; - // let data = await OrgService.getWithFeatures({ - // db: req.db, - // orgId: tokenOrg.id, - // env: req.env, - // }); + let data = await OrgService.getWithFeatures({ + db: req.db, + orgId: orgId, + env: req.env, + }); - // if (!data) { - // return res.status(404).json({ message: "Org not found" }); - // } + if (!data) { + logger.warn(`Org ${orgId} not found in DB`); + return res + .status(500) + .json({ message: "Org not found", code: ErrCode.OrgNotFound }); + } - // const { org, features } = data; + const { org, features } = data; - // req.minOrg = { - // id: tokenOrg?.id, - // slug: tokenOrg?.slug, - // }; - - // req.orgId = tokenData!.org_id; - // req.user = tokenData!.user; - // req.org = org; - // req.features = features; - // req.authType = AuthType.Dashboard; + req.user = session?.user; + req.orgId = orgId; + req.org = org; + req.features = features; + req.authType = AuthType.Dashboard; next(); } catch (error: any) { - console.log( - // `withOrgAuth error (${req.headers["authorization"]}):`, - `(warning) clerk auth failed:`, - error?.message || error, - ); + // console.log(`(warning) clerk auth failed:`, error?.message || error); + logger.warn(`(warning) withOrgAuth failed:`, error?.message || error); res.status(401).json({ message: "Unauthorized" }); return; } diff --git a/server/src/utils/auth.ts b/server/src/utils/auth.ts index cf2d8bc1d..861ff9dc3 100644 --- a/server/src/utils/auth.ts +++ b/server/src/utils/auth.ts @@ -2,13 +2,64 @@ import { betterAuth } from "better-auth"; import { emailOTP, admin, organization } from "better-auth/plugins"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; -import { authDb } from "@/db/initDrizzle.js"; // your drizzle instance +// import { authDb } from "@/db/initDrizzle.js"; // your drizzle instance import { saveOrgToDB } from "@/external/webhooks/clerkWebhooks.js"; +import { db } from "@/db/initDrizzle.js"; +import { member, session as sessionTable } from "@autumn/shared"; +import { desc, eq } from "drizzle-orm"; export const auth = betterAuth({ - database: drizzleAdapter(authDb, { + database: drizzleAdapter(db, { provider: "pg", // or "mysql", "sqlite" }), + databaseHooks: { + session: { + create: { + before: async (session) => { + let lastSession = await db + .select() + .from(sessionTable) + .where(eq(sessionTable.userId, session.userId)) + .orderBy(desc(sessionTable.createdAt)) + .limit(1); + + if (!lastSession) { + return { + data: { + ...session, + activeOrganizationId: null, + }, + }; + } + + if (lastSession[0].activeOrganizationId) { + return { + data: { + ...session, + activeOrganizationId: lastSession[0].activeOrganizationId, + }, + }; + } + + let memberships = await db + .select() + .from(member) + .where(eq(member.userId, session.userId)); + + if (memberships.length > 0) { + return { + data: { + ...session, + activeOrganizationId: memberships[0].organizationId, + }, + }; + } + + return { data: session }; + }, + }, + }, + }, user: { deleteUser: { enabled: true, @@ -41,11 +92,11 @@ export const auth = betterAuth({ organizationCreation: { disabled: false, afterCreate: async ({ organization, user }) => { - // await saveOrgToDB({ - // db, - // id: org.id, - // slug: org.slug, - // }); + await saveOrgToDB({ + db, + id: organization.id, + slug: organization.slug, + }); }, }, }), diff --git a/server/src/utils/errorUtils.ts b/server/src/utils/errorUtils.ts index a5ba71074..c4cced51f 100644 --- a/server/src/utils/errorUtils.ts +++ b/server/src/utils/errorUtils.ts @@ -113,9 +113,7 @@ export const handleRequestError = ({ // logger.warn(`${req.method} ${req.originalUrl}`); logReqUrl(logger, req, "warn"); logger.warn( - `Request from ${ - req.minOrg?.slug || req.org?.slug || req.orgId || "unknown" - } for ${action}`, + `Request from ${req.org?.slug || req.orgId || "unknown"} for ${action}`, ); error.print(logger); if (req.originalUrl.includes("/webhooks/stripe")) { @@ -139,9 +137,7 @@ export const handleRequestError = ({ // logger.error(`${req.method} ${req.originalUrl}`); logReqUrl(logger, req, "error"); logger.error( - `Request from ${ - req.minOrg?.slug || req.org?.slug || req.orgId || "unknown" - } for ${action}`, + `Request from ${req.org?.slug || req.orgId || "unknown"} for ${action}`, ); if (error instanceof Stripe.errors.StripeError) { diff --git a/server/src/utils/models/Request.ts b/server/src/utils/models/Request.ts index 943c0973a..a389d1a15 100644 --- a/server/src/utils/models/Request.ts +++ b/server/src/utils/models/Request.ts @@ -1,10 +1,4 @@ -import { - AppEnv, - AuthType, - Feature, - MinOrg, - Organization, -} from "@autumn/shared"; +import { AppEnv, AuthType, Feature, Organization } from "@autumn/shared"; import { Logtail } from "@logtail/node"; import type { Request as ExpressRequest, @@ -27,7 +21,6 @@ export interface ExtendedRequest extends ExpressRequest { orgId: string; env: AppEnv; - minOrg: MinOrg; org: Organization; features: Feature[]; diff --git a/server/src/utils/routerUtils.ts b/server/src/utils/routerUtils.ts index 5ba45d798..27de0badc 100644 --- a/server/src/utils/routerUtils.ts +++ b/server/src/utils/routerUtils.ts @@ -24,7 +24,9 @@ export const routeHandler = async ({ try { if (error instanceof RecaseError) { if (error.code === ErrCode.EntityNotFound) { - req.logtail.warn(`${error.message}, org: ${req.minOrg?.slug}`); + req.logtail.warn( + `${error.message}, org: ${req.org?.slug || req.orgId}`, + ); return res.status(404).json({ message: error.message, code: error.code, diff --git a/server/tsconfig.json b/server/tsconfig.json index c8456d24f..d0ccda1b0 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -8,7 +8,7 @@ "moduleResolution": "NodeNext", // or "node16"/"nodenext" "module": "NodeNext", // or "node16"/"nodenext" - "declaration": true, + // "declaration": true, "rootDir": ".", "baseUrl": ".", "outDir": "./dist", diff --git a/shared/archives/orgModels_old.ts b/shared/archives/orgModels_old.ts index 56532a3d5..a82709468 100644 --- a/shared/archives/orgModels_old.ts +++ b/shared/archives/orgModels_old.ts @@ -3,11 +3,6 @@ import { z } from "zod"; import { OrgConfigSchema } from "../models/orgModels/orgConfig.js"; -export const MinOrgSchema = z.object({ - id: z.string(), - slug: z.string(), -}); - export const StripeConfigSchema = z.object({ test_api_key: z.string(), live_api_key: z.string(), @@ -41,7 +36,6 @@ export const OrganizationSchema = z.object({ api_version: z.number().nullish(), }); -export type MinOrg = z.infer; export type Organization = z.infer; export type StripeConfig = z.infer; export type SvixConfig = z.infer; diff --git a/shared/db/auth-schema.ts b/shared/db/auth-schema.ts index 25770480c..b98efcead 100644 --- a/shared/db/auth-schema.ts +++ b/shared/db/auth-schema.ts @@ -40,7 +40,7 @@ export const session = pgTable("session", { .references(() => user.id, { onDelete: "cascade" }), impersonatedBy: text("impersonated_by"), activeOrganizationId: text("active_organization_id"), -}); +}).enableRLS(); export const account = pgTable("account", { id: text("id").primaryKey(), @@ -58,7 +58,7 @@ export const account = pgTable("account", { password: text("password"), createdAt: timestamp("created_at").notNull(), updatedAt: timestamp("updated_at").notNull(), -}); +}).enableRLS(); export const verification = pgTable("verification", { id: text("id").primaryKey(), @@ -71,7 +71,7 @@ export const verification = pgTable("verification", { updatedAt: timestamp("updated_at").$defaultFn( () => /* @__PURE__ */ new Date(), ), -}); +}).enableRLS(); // export const organization = pgTable("organization", { // id: text("id").primaryKey(), @@ -92,7 +92,7 @@ export const member = pgTable("member", { .references(() => user.id, { onDelete: "cascade" }), role: text("role").default("member").notNull(), createdAt: timestamp("created_at").notNull(), -}); +}).enableRLS(); export const invitation = pgTable("invitation", { id: text("id").primaryKey(), @@ -106,7 +106,7 @@ export const invitation = pgTable("invitation", { inviterId: text("inviter_id") .notNull() .references(() => user.id, { onDelete: "cascade" }), -}); +}).enableRLS(); export const authSchema = { user, diff --git a/shared/db/schema.ts b/shared/db/schema.ts index 0e2f24704..148a8f26c 100644 --- a/shared/db/schema.ts +++ b/shared/db/schema.ts @@ -68,7 +68,14 @@ import { actions } from "../models/analyticsModels/actionTable.js"; import { events } from "../models/eventModels/eventTable.js"; import { replaceables } from "../models/cusProductModels/cusEntModels/replaceableTable.js"; -import { user, session, account, verification, member } from "./auth-schema.js"; +import { + user, + session, + account, + verification, + member, + invitation, +} from "./auth-schema.js"; export { // Tables @@ -98,6 +105,14 @@ export { events, replaceables, + // Auth + user, + session, + account, + verification, + member, + invitation, + // Relations organizationsRelations, entitlementsRelations, diff --git a/shared/drizzle/0008_fresh_jackpot.sql b/shared/drizzle/0008_fresh_jackpot.sql new file mode 100644 index 000000000..64d2d8c90 --- /dev/null +++ b/shared/drizzle/0008_fresh_jackpot.sql @@ -0,0 +1,17 @@ +CREATE TABLE "invitation" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "email" text NOT NULL, + "role" text, + "status" text DEFAULT 'pending' NOT NULL, + "expires_at" timestamp NOT NULL, + "inviter_id" text NOT NULL +); +--> statement-breakpoint +ALTER TABLE "invitation" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint +ALTER TABLE "account" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint +ALTER TABLE "member" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint +ALTER TABLE "session" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint +ALTER TABLE "verification" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint +ALTER TABLE "invitation" ADD CONSTRAINT "invitation_organization_id_organizations_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "invitation" ADD CONSTRAINT "invitation_inviter_id_user_id_fk" FOREIGN KEY ("inviter_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/shared/drizzle/meta/0008_snapshot.json b/shared/drizzle/meta/0008_snapshot.json new file mode 100644 index 000000000..a654e6eab --- /dev/null +++ b/shared/drizzle/meta/0008_snapshot.json @@ -0,0 +1,3106 @@ +{ + "id": "f20249cb-3add-49f3-a66c-3fa814ca2b29", + "prevId": "272cae26-209d-4f72-bf03-2a0c508f03da", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": true + }, + "public.actions": { + "name": "actions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_slug": { + "name": "org_slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "internal_entity_id": { + "name": "internal_entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "method": { + "name": "method", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "properties": { + "name": "properties", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "actions_org_id_fkey": { + "name": "actions_org_id_fkey", + "tableFrom": "actions", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "actions_customer_id_fkey": { + "name": "actions_customer_id_fkey", + "tableFrom": "actions", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "actions_entity_id_fkey": { + "name": "actions_entity_id_fkey", + "tableFrom": "actions", + "tableTo": "entities", + "columnsFrom": [ + "internal_entity_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": true + }, + "public.api_keys": { + "name": "api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "ROUND(date_part('epoch', NOW()) * 1000)::BIGINT" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "hashed_key": { + "name": "hashed_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "meta": { + "name": "meta", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "api_keys_org_id_fkey": { + "name": "api_keys_org_id_fkey", + "tableFrom": "api_keys", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_hashed_key_key": { + "name": "api_keys_hashed_key_key", + "nullsNotDistinct": false, + "columns": [ + "hashed_key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_results": { + "name": "chat_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": true + }, + "public.customer_entitlements": { + "name": "customer_entitlements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "customer_product_id": { + "name": "customer_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entitlement_id": { + "name": "entitlement_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_feature_id": { + "name": "internal_feature_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "unlimited": { + "name": "unlimited", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "balance": { + "name": "balance", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "next_reset_at": { + "name": "next_reset_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "usage_allowed": { + "name": "usage_allowed", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "adjustment": { + "name": "adjustment", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "entities": { + "name": "entities", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feature_id": { + "name": "feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "entitlements_internal_feature_id_fkey": { + "name": "entitlements_internal_feature_id_fkey", + "tableFrom": "customer_entitlements", + "tableTo": "features", + "columnsFrom": [ + "internal_feature_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "customer_entitlements_customer_product_id_fkey": { + "name": "customer_entitlements_customer_product_id_fkey", + "tableFrom": "customer_entitlements", + "tableTo": "customer_products", + "columnsFrom": [ + "customer_product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "entitlements_id_key": { + "name": "entitlements_id_key", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.customer_prices": { + "name": "customer_prices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "price_id": { + "name": "price_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "options": { + "name": "options", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_product_id": { + "name": "customer_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "customer_prices_customer_product_id_fkey": { + "name": "customer_prices_customer_product_id_fkey", + "tableFrom": "customer_prices", + "tableTo": "customer_products", + "columnsFrom": [ + "customer_product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "customer_prices_internal_customer_id_fkey": { + "name": "customer_prices_internal_customer_id_fkey", + "tableFrom": "customer_prices", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "customer_prices_price_id_fkey": { + "name": "customer_prices_price_id_fkey", + "tableFrom": "customer_prices", + "tableTo": "prices", + "columnsFrom": [ + "price_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.customer_products": { + "name": "customer_products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_product_id": { + "name": "internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_entity_id": { + "name": "internal_entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "processor": { + "name": "processor", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "canceled_at": { + "name": "canceled_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "ended_at": { + "name": "ended_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "starts_at": { + "name": "starts_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "options": { + "name": "options", + "type": "jsonb[]", + "primaryKey": false, + "notNull": false + }, + "product_id": { + "name": "product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "free_trial_id": { + "name": "free_trial_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "trial_ends_at": { + "name": "trial_ends_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "collection_method": { + "name": "collection_method", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'charge_automatically'" + }, + "subscription_ids": { + "name": "subscription_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "scheduled_ids": { + "name": "scheduled_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "quantity": { + "name": "quantity", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "is_custom": { + "name": "is_custom", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "api_version": { + "name": "api_version", + "type": "numeric", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "customer_products_free_trial_id_fkey": { + "name": "customer_products_free_trial_id_fkey", + "tableFrom": "customer_products", + "tableTo": "free_trials", + "columnsFrom": [ + "free_trial_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "customer_products_internal_customer_id_fkey": { + "name": "customer_products_internal_customer_id_fkey", + "tableFrom": "customer_products", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "customer_products_internal_product_id_fkey": { + "name": "customer_products_internal_product_id_fkey", + "tableFrom": "customer_products", + "tableTo": "products", + "columnsFrom": [ + "internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "customer_products_internal_entity_id_fkey": { + "name": "customer_products_internal_entity_id_fkey", + "tableFrom": "customer_products", + "tableTo": "entities", + "columnsFrom": [ + "internal_entity_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.customers": { + "name": "customers", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fingerprint": { + "name": "fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "null" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "processor": { + "name": "processor", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "customers_org_id_fkey": { + "name": "customers_org_id_fkey", + "tableFrom": "customers", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cus_id_constraint": { + "name": "cus_id_constraint", + "nullsNotDistinct": false, + "columns": [ + "org_id", + "id", + "env" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": true + }, + "public.entities": { + "name": "entities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "internal_id": { + "name": "internal_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "internal_feature_id": { + "name": "internal_feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feature_id": { + "name": "feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "entities_internal_customer_id_fkey": { + "name": "entities_internal_customer_id_fkey", + "tableFrom": "entities", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "entities_internal_feature_id_fkey": { + "name": "entities_internal_feature_id_fkey", + "tableFrom": "entities", + "tableTo": "features", + "columnsFrom": [ + "internal_feature_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "entities_org_id_fkey": { + "name": "entities_org_id_fkey", + "tableFrom": "entities", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "entity_id_constraint": { + "name": "entity_id_constraint", + "nullsNotDistinct": false, + "columns": [ + "org_id", + "env", + "internal_customer_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.entitlements": { + "name": "entitlements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "internal_feature_id": { + "name": "internal_feature_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_product_id": { + "name": "internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_custom": { + "name": "is_custom", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "allowance_type": { + "name": "allowance_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allowance": { + "name": "allowance", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "interval": { + "name": "interval", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "carry_from_previous": { + "name": "carry_from_previous", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "entity_feature_id": { + "name": "entity_feature_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "null" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feature_id": { + "name": "feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "entitlements_internal_feature_id_fkey": { + "name": "entitlements_internal_feature_id_fkey", + "tableFrom": "entitlements", + "tableTo": "features", + "columnsFrom": [ + "internal_feature_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "entitlements_internal_product_id_fkey": { + "name": "entitlements_internal_product_id_fkey", + "tableFrom": "entitlements", + "tableTo": "products", + "columnsFrom": [ + "internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "entitlements_id_key": { + "name": "entitlements_id_key", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.events": { + "name": "events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_slug": { + "name": "org_slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "event_name": { + "name": "event_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "null" + }, + "value": { + "name": "value", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "set_usage": { + "name": "set_usage", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "properties": { + "name": "properties", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "events_internal_customer_id_fkey": { + "name": "events_internal_customer_id_fkey", + "tableFrom": "events", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_event_constraint": { + "name": "unique_event_constraint", + "nullsNotDistinct": false, + "columns": [ + "org_id", + "env", + "customer_id", + "event_name", + "idempotency_key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.features": { + "name": "features", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "display": { + "name": "display", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "null" + } + }, + "indexes": {}, + "foreignKeys": { + "features_org_id_fkey": { + "name": "features_org_id_fkey", + "tableFrom": "features", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "feature_id_constraint": { + "name": "feature_id_constraint", + "nullsNotDistinct": false, + "columns": [ + "org_id", + "id", + "env" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.free_trials": { + "name": "free_trials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "internal_product_id": { + "name": "internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "duration": { + "name": "duration", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'day'" + }, + "length": { + "name": "length", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "unique_fingerprint": { + "name": "unique_fingerprint", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "is_custom": { + "name": "is_custom", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "free_trials_internal_product_id_fkey": { + "name": "free_trials_internal_product_id_fkey", + "tableFrom": "free_trials", + "tableTo": "products", + "columnsFrom": [ + "internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organizations_id_fk": { + "name": "invitation_organization_id_organizations_id_fk", + "tableFrom": "invitation", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_id_fk": { + "name": "invitation_inviter_id_user_id_fk", + "tableFrom": "invitation", + "tableTo": "user", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": true + }, + "public.invoices": { + "name": "invoices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "ROUND(date_part('epoch', NOW()) * 1000)::BIGINT" + }, + "product_ids": { + "name": "product_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "internal_product_ids": { + "name": "internal_product_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_entity_id": { + "name": "internal_entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_id": { + "name": "stripe_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "hosted_invoice_url": { + "name": "hosted_invoice_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "total": { + "name": "total", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'usd'" + }, + "discounts": { + "name": "discounts", + "type": "jsonb[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "items": { + "name": "items", + "type": "jsonb[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + } + }, + "indexes": {}, + "foreignKeys": { + "invoices_internal_customer_id_fkey": { + "name": "invoices_internal_customer_id_fkey", + "tableFrom": "invoices", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "invoices_internal_entity_id_fkey": { + "name": "invoices_internal_entity_id_fkey", + "tableFrom": "invoices", + "tableTo": "entities", + "columnsFrom": [ + "internal_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organizations_id_fk": { + "name": "member_organization_id_organizations_id_fk", + "tableFrom": "member", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_user_id_user_id_fk": { + "name": "member_user_id_user_id_fk", + "tableFrom": "member", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": true + }, + "public.metadata": { + "name": "metadata", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "ROUND(date_part('epoch', NOW()) * 1000)::BIGINT" + }, + "expires_at": { + "name": "expires_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.migration_errors": { + "name": "migration_errors", + "schema": "", + "columns": { + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "migration_job_id": { + "name": "migration_job_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "migration_customers_internal_customer_id_fkey": { + "name": "migration_customers_internal_customer_id_fkey", + "tableFrom": "migration_errors", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "migration_customers_migration_job_id_fkey": { + "name": "migration_customers_migration_job_id_fkey", + "tableFrom": "migration_errors", + "tableTo": "migration_jobs", + "columnsFrom": [ + "migration_job_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "migration_errors_pkey": { + "name": "migration_errors_pkey", + "columns": [ + "internal_customer_id", + "migration_job_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.migration_jobs": { + "name": "migration_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "current_step": { + "name": "current_step", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "from_internal_product_id": { + "name": "from_internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "to_internal_product_id": { + "name": "to_internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "step_details": { + "name": "step_details", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "migration_jobs_from_internal_product_id_fkey": { + "name": "migration_jobs_from_internal_product_id_fkey", + "tableFrom": "migration_jobs", + "tableTo": "products", + "columnsFrom": [ + "from_internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "migration_jobs_org_id_fkey": { + "name": "migration_jobs_org_id_fkey", + "tableFrom": "migration_jobs", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "migration_jobs_to_internal_product_id_fkey": { + "name": "migration_jobs_to_internal_product_id_fkey", + "tableFrom": "migration_jobs", + "tableTo": "products", + "columnsFrom": [ + "to_internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organizations": { + "name": "organizations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "default_currency": { + "name": "default_currency", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'usd'" + }, + "stripe_connected": { + "name": "stripe_connected", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "stripe_config": { + "name": "stripe_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "test_pkey": { + "name": "test_pkey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "live_pkey": { + "name": "live_pkey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "svix_config": { + "name": "svix_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organizations_slug_unique": { + "name": "organizations_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + }, + "organizations_test_pkey_key": { + "name": "organizations_test_pkey_key", + "nullsNotDistinct": false, + "columns": [ + "test_pkey" + ] + }, + "organizations_live_pkey_key": { + "name": "organizations_live_pkey_key", + "nullsNotDistinct": false, + "columns": [ + "live_pkey" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.prices": { + "name": "prices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_product_id": { + "name": "internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "billing_type": { + "name": "billing_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_custom": { + "name": "is_custom", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "entitlement_id": { + "name": "entitlement_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "null" + }, + "proration_config": { + "name": "proration_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "null" + } + }, + "indexes": {}, + "foreignKeys": { + "prices_entitlement_id_fkey": { + "name": "prices_entitlement_id_fkey", + "tableFrom": "prices", + "tableTo": "entitlements", + "columnsFrom": [ + "entitlement_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "prices_internal_product_id_fkey": { + "name": "prices_internal_product_id_fkey", + "tableFrom": "prices", + "tableTo": "products", + "columnsFrom": [ + "internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "prices_id_key": { + "name": "prices_id_key", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.products": { + "name": "products", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "ROUND(date_part('epoch', NOW()) * 1000)::BIGINT" + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_add_on": { + "name": "is_add_on", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "group": { + "name": "group", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "version": { + "name": "version", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "processor": { + "name": "processor", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "null" + } + }, + "indexes": {}, + "foreignKeys": { + "products_org_id_fkey": { + "name": "products_org_id_fkey", + "tableFrom": "products", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_product": { + "name": "unique_product", + "nullsNotDistinct": false, + "columns": [ + "org_id", + "id", + "env", + "version" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.referral_codes": { + "name": "referral_codes", + "schema": "", + "columns": { + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "internal_reward_program_id": { + "name": "internal_reward_program_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "referral_codes_internal_customer_id_fkey": { + "name": "referral_codes_internal_customer_id_fkey", + "tableFrom": "referral_codes", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "referral_codes_internal_reward_program_id_fkey": { + "name": "referral_codes_internal_reward_program_id_fkey", + "tableFrom": "referral_codes", + "tableTo": "reward_programs", + "columnsFrom": [ + "internal_reward_program_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "referral_codes_org_id_fkey": { + "name": "referral_codes_org_id_fkey", + "tableFrom": "referral_codes", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "referral_codes_pkey": { + "name": "referral_codes_pkey", + "columns": [ + "code", + "org_id", + "env" + ] + } + }, + "uniqueConstraints": { + "referral_codes_id_key": { + "name": "referral_codes_id_key", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.replaceables": { + "name": "replaceables", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "cus_ent_id": { + "name": "cus_ent_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from_entity_id": { + "name": "from_entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "delete_next_cycle": { + "name": "delete_next_cycle", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "replaceables_cus_ent_id_fkey": { + "name": "replaceables_cus_ent_id_fkey", + "tableFrom": "replaceables", + "tableTo": "customer_entitlements", + "columnsFrom": [ + "cus_ent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": true + }, + "public.reward_programs": { + "name": "reward_programs", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "internal_reward_id": { + "name": "internal_reward_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "max_redemptions": { + "name": "max_redemptions", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "unlimited_redemptions": { + "name": "unlimited_redemptions", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "when": { + "name": "when", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'immediately'" + }, + "product_ids": { + "name": "product_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{\"\"}'" + }, + "exclude_trial": { + "name": "exclude_trial", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "received_by": { + "name": "received_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "reward_triggers_internal_reward_id_fkey": { + "name": "reward_triggers_internal_reward_id_fkey", + "tableFrom": "reward_programs", + "tableTo": "rewards", + "columnsFrom": [ + "internal_reward_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reward_triggers_org_id_fkey": { + "name": "reward_triggers_org_id_fkey", + "tableFrom": "reward_programs", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reward_redemptions": { + "name": "reward_redemptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "triggered": { + "name": "triggered", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "internal_reward_program_id": { + "name": "internal_reward_program_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applied": { + "name": "applied", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "referral_code_id": { + "name": "referral_code_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "reward_redemptions_internal_customer_id_fkey": { + "name": "reward_redemptions_internal_customer_id_fkey", + "tableFrom": "reward_redemptions", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reward_redemptions_internal_reward_program_id_fkey": { + "name": "reward_redemptions_internal_reward_program_id_fkey", + "tableFrom": "reward_redemptions", + "tableTo": "reward_programs", + "columnsFrom": [ + "internal_reward_program_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reward_redemptions_referral_code_id_fkey": { + "name": "reward_redemptions_referral_code_id_fkey", + "tableFrom": "reward_redemptions", + "tableTo": "referral_codes", + "columnsFrom": [ + "referral_code_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rewards": { + "name": "rewards", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discount_config": { + "name": "discount_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "free_product_id": { + "name": "free_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "promo_codes": { + "name": "promo_codes", + "type": "jsonb[]", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "coupons_org_id_fkey": { + "name": "coupons_org_id_fkey", + "tableFrom": "rewards", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": true + }, + "public.subscriptions": { + "name": "subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_id": { + "name": "stripe_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_schedule_id": { + "name": "stripe_schedule_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "ROUND(date_part('epoch', NOW()) * 1000)::BIGINT" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "usage_features": { + "name": "usage_features", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "current_period_start": { + "name": "current_period_start", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "current_period_end": { + "name": "current_period_end", + "type": "numeric", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "subscriptions_org_id_fkey": { + "name": "subscriptions_org_id_fkey", + "tableFrom": "subscriptions", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "subscriptions_stripe_id_key": { + "name": "subscriptions_stripe_id_key", + "nullsNotDistinct": false, + "columns": [ + "stripe_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": true + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/shared/drizzle/meta/_journal.json b/shared/drizzle/meta/_journal.json index 538dc9a42..dc2abb8f0 100644 --- a/shared/drizzle/meta/_journal.json +++ b/shared/drizzle/meta/_journal.json @@ -43,6 +43,27 @@ "when": 1749746944226, "tag": "0005_young_wendell_rand", "breakpoints": true + }, + { + "idx": 6, + "version": "7", + "when": 1749752804406, + "tag": "0006_bizarre_maestro", + "breakpoints": true + }, + { + "idx": 7, + "version": "7", + "when": 1749752870654, + "tag": "0007_slim_talisman", + "breakpoints": true + }, + { + "idx": 8, + "version": "7", + "when": 1749752911832, + "tag": "0008_fresh_jackpot", + "breakpoints": true } ] } \ No newline at end of file diff --git a/shared/index.ts b/shared/index.ts index a62eae3fe..64cfc5dc8 100644 --- a/shared/index.ts +++ b/shared/index.ts @@ -10,6 +10,7 @@ export * from "./models/genModels/genModels.js"; // 1. Org Models export * from "./models/orgModels/orgTable.js"; export * from "./models/orgModels/orgConfig.js"; +export * from "./models/orgModels/frontendOrg.js"; // 2. Feature Models export * from "./models/featureModels/featureTable.js"; diff --git a/shared/models/orgModels/frontendOrg.ts b/shared/models/orgModels/frontendOrg.ts new file mode 100644 index 000000000..a398e2c09 --- /dev/null +++ b/shared/models/orgModels/frontendOrg.ts @@ -0,0 +1,15 @@ +import { z } from "zod"; + +export const FrontendOrgSchema = z.object({ + id: z.string(), + name: z.string(), + logo: z.string().nullable(), + slug: z.string(), + default_currency: z.string(), + stripe_connected: z.boolean(), + created_at: z.number(), + test_pkey: z.string().nullable(), + live_pkey: z.string().nullable(), +}); + +export type FrontendOrg = z.infer; diff --git a/shared/models/orgModels/orgTable.ts b/shared/models/orgModels/orgTable.ts index c07bcb5b0..f6f84f401 100644 --- a/shared/models/orgModels/orgTable.ts +++ b/shared/models/orgModels/orgTable.ts @@ -11,11 +11,6 @@ import { import { OrgConfig } from "./orgConfig.js"; import { sql } from "drizzle-orm"; -export type MinOrg = { - id: string; - slug: string; -}; - export type SvixConfig = { sandbox_app_id: string; live_app_id: string; diff --git a/vite/src/app/layout.tsx b/vite/src/app/layout.tsx index 987658190..afb0da831 100644 --- a/vite/src/app/layout.tsx +++ b/vite/src/app/layout.tsx @@ -5,8 +5,6 @@ import { getRedirectUrl, navigateTo } from "@/utils/genUtils"; import LoadingScreen from "@/views/general/LoadingScreen"; import { MainSidebar } from "@/views/main-sidebar/MainSidebar"; import { AppEnv } from "@autumn/shared"; -import { RedirectToSignIn, useUser } from "@clerk/clerk-react"; -import { useOrganization } from "@clerk/clerk-react"; import { useEffect } from "react"; import { Navigate, Outlet, useLocation, useNavigate } from "react-router"; @@ -19,15 +17,14 @@ import { useSession } from "@/lib/auth-client"; export function MainLayout() { const env = useEnv(); - // const { isLoaded: isUserLoaded, user } = useUser(); - // const { organization: org } = useOrganization(); + const { getToken } = useAuth(); const { pathname } = useLocation(); + const { data, isPending } = useSession(); const navigate = useNavigate(); - const posthog = usePostHog(); - const { data, isPending } = useSession(); + const orgId = data?.session.activeOrganizationId; useEffect(() => { // Identify user @@ -81,7 +78,7 @@ export function MainLayout() { return; } - if (!pathname.includes("/onboarding")) { + if (!orgId && !pathname.includes("/onboarding")) { return ( { - const { isLoaded, user } = useUser(); - const { actor } = useAuth(); + const { data, isPending } = useSession(); - const email = user?.primaryEmailAddress?.emailAddress; + const user = data?.user; + + // const { isLoaded, user } = useUser(); + // const { actor } = useAuth(); + + const email = user?.email; const isAdmin = - notNullish(actor) || + // notNullish(actor) || email === "johnyeocx@gmail.com" || email === "ayush@recaseai.com" || email === "johnyeo10@gmail.com" || @@ -36,7 +39,7 @@ export const AdminHover = ({ {children} - {isLoaded && ( + {!isPending && ( ( > {isLoading && } {startIcon && !isLoading && <>{startIcon}} - {variant == "add" && !disableStartIcon && } + {!isLoading && variant == "add" && !disableStartIcon && ( + + )} {children} {endIcon && !isLoading && <>{endIcon}} diff --git a/vite/src/components/ui/dialog.tsx b/vite/src/components/ui/dialog.tsx index 0fbcaf2e9..f347c8811 100644 --- a/vite/src/components/ui/dialog.tsx +++ b/vite/src/components/ui/dialog.tsx @@ -39,7 +39,7 @@ const DialogOverlay = React.forwardRef< data-slot="dialog-overlay" className={cn( "fixed inset-0 z-50 bg-white/70 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", - className + className, )} {...props} /> @@ -62,7 +62,7 @@ function DialogContent({ bg-stone-50 min-w-sm `, - className + className, )} {...props} > @@ -86,13 +86,19 @@ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { ); } -function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { +function DialogFooter({ + className, + variant = "default", + ...props +}: React.ComponentProps<"div"> & { variant?: "new" | "default" }) { return (
diff --git a/vite/src/components/ui/dropdown-menu.tsx b/vite/src/components/ui/dropdown-menu.tsx index b7fee7588..7ee25d34c 100644 --- a/vite/src/components/ui/dropdown-menu.tsx +++ b/vite/src/components/ui/dropdown-menu.tsx @@ -23,19 +23,20 @@ const DropdownMenuSubTrigger = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef & { inset?: boolean; + withIcon?: boolean; } ->(({ className, inset, children, ...props }, ref) => ( +>(({ className, inset, children, withIcon = true, ...props }, ref) => ( {children} - + {withIcon && } )); DropdownMenuSubTrigger.displayName = @@ -49,7 +50,7 @@ const DropdownMenuSubContent = React.forwardRef< ref={ref} className={cn( "z-50 min-w-[8rem] overflow-hidden rounded-md border border-zinc-200 bg-white p-1 text-zinc-950 shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50", - className + className, )} {...props} /> @@ -69,7 +70,7 @@ const DropdownMenuContent = React.forwardRef< className={cn( "z-50 min-w-[8rem] overflow-hidden rounded-md border border-zinc-200 bg-white p-1 text-zinc-950 shadow-md dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50", "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", - className + className, )} {...props} /> @@ -82,8 +83,9 @@ const DropdownMenuItem = React.forwardRef< React.ComponentPropsWithoutRef & { inset?: boolean; isLoading?: boolean; + shimmer?: boolean; } ->(({ className, inset, isLoading, ...props }, ref) => ( +>(({ className, inset, shimmer = false, isLoading, ...props }, ref) => ( {isLoading ? ( @@ -101,6 +104,8 @@ const DropdownMenuItem = React.forwardRef< {props.children} + ) : shimmer ? ( +
{props.children}
) : ( props.children )} @@ -116,7 +121,7 @@ const DropdownMenuCheckboxItem = React.forwardRef< ref={ref} className={cn( "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-zinc-100 focus:text-zinc-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-zinc-800 dark:focus:text-zinc-50", - className + className, )} checked={checked} {...props} @@ -140,7 +145,7 @@ const DropdownMenuRadioItem = React.forwardRef< ref={ref} className={cn( "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-zinc-100 focus:text-zinc-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-zinc-800 dark:focus:text-zinc-50", - className + className, )} {...props} > @@ -165,7 +170,7 @@ const DropdownMenuLabel = React.forwardRef< className={cn( "px-2 py-1.5 text-sm font-semibold", inset && "pl-8", - className + className, )} {...props} /> diff --git a/vite/src/hooks/useOrg.tsx b/vite/src/hooks/useOrg.tsx new file mode 100644 index 000000000..219f9038c --- /dev/null +++ b/vite/src/hooks/useOrg.tsx @@ -0,0 +1,28 @@ +import { + authClient, + useListOrganizations, + useSession, +} from "@/lib/auth-client"; +import { useAxiosSWR } from "@/services/useAxiosSwr"; +import { FrontendOrg } from "@autumn/shared"; +import { useEffect } from "react"; + +export const useOrg = () => { + const { data, isLoading, mutate } = useAxiosSWR({ + url: "/organization", + }); + + // const { data: orgList } = useListOrganizations(); + + // useEffect(() => { + // if (!data && orgList?.length === 1) { + // authClient.organization.setActive({ + // organizationId: orgList[0].id, + // }); + + // mutate(); + // } + // }, [data, orgList]); + + return { org: data as FrontendOrg, isLoading, mutate }; +}; diff --git a/vite/src/hooks/useOrgId.tsx b/vite/src/hooks/useOrgId.tsx new file mode 100644 index 000000000..5fd184c8c --- /dev/null +++ b/vite/src/hooks/useOrgId.tsx @@ -0,0 +1,46 @@ +import { + authClient, + useListOrganizations, + useSession, +} from "@/lib/auth-client"; +import { useAxiosSWR } from "@/services/useAxiosSwr"; +import { FrontendOrg } from "@autumn/shared"; +import { useEffect, useState } from "react"; + +export const useOrgId = () => { + const { data: session, isPending: sessionPending } = useSession(); + const { data: organizations, isPending: orgsPending } = + useListOrganizations(); + + const [orgId, setOrgId] = useState(null); + + const setFirstOrg = async () => { + if (organizations?.length === 1) { + await authClient.organization.setActive({ + organizationId: organizations[0].id, + }); + } + }; + + useEffect(() => { + const activeId = session?.session.activeOrganizationId; + if (activeId) { + setOrgId(activeId); + } + }, [session]); + + // useEffect(() => { + // if (!orgId && organizations?.length && organizations.length > 0) { + // setFirstOrg(); + // } + // }, [session, organizations]); + + useEffect(() => { + console.log("OrgId", orgId); + }, [orgId]); + + return { + orgId, + isLoading: sessionPending, + }; +}; diff --git a/vite/src/hooks/useSessionClaims.tsx b/vite/src/hooks/useSessionClaims.tsx deleted file mode 100644 index b37177106..000000000 --- a/vite/src/hooks/useSessionClaims.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { useOrganization, useSession } from "@clerk/clerk-react"; -import { useEffect } from "react"; - -export const useSessionClaims = () => { - const { isLoaded, session } = useSession(); - const { isLoaded: isOrgLoaded, organization } = useOrganization(); - useEffect(() => {}, [isOrgLoaded, isLoaded]); - - if (!isLoaded) { - return { isLoaded: false, claims: null }; - } - - return { isLoaded: true, claims: session?.lastActiveToken?.jwt?.claims }; -}; diff --git a/vite/src/index.css b/vite/src/index.css index 40c64975e..3897df466 100644 --- a/vite/src/index.css +++ b/vite/src/index.css @@ -255,3 +255,29 @@ button:focus-visible { button { cursor: pointer; } + +.shimmer { + position: relative; + overflow: hidden; + /* background-color: #e5e7eb; */ +} +.shimmer::after { + content: ""; + position: absolute; + top: 0; + left: -150%; + width: 100%; + height: 100%; + background: linear-gradient( + 90deg, + transparent, + rgba(255, 255, 255, 0.6), + transparent + ); + animation: shimmer 1.5s infinite; +} +@keyframes shimmer { + 100% { + left: 150%; + } +} diff --git a/vite/src/lib/auth-client.ts b/vite/src/lib/auth-client.ts index 8af9e74e2..5cba9e15d 100644 --- a/vite/src/lib/auth-client.ts +++ b/vite/src/lib/auth-client.ts @@ -2,8 +2,19 @@ import { createAuthClient } from "better-auth/react"; import { emailOTPClient } from "better-auth/client/plugins"; import { organizationClient } from "better-auth/client/plugins"; -export const { useSession, signIn, signUp, signOut, deleteUser } = - createAuthClient({ - baseURL: "http://localhost:8080", - plugins: [emailOTPClient(), organizationClient()], - }); +export const authClient = createAuthClient({ + baseURL: "http://localhost:8080", + plugins: [emailOTPClient(), organizationClient()], +}); + +export const { + useSession, + signIn, + signUp, + signOut, + deleteUser, + useListOrganizations, +} = createAuthClient({ + baseURL: "http://localhost:8080", + plugins: [emailOTPClient(), organizationClient()], +}); diff --git a/vite/src/main.tsx b/vite/src/main.tsx index a10db9b00..b53a8dde5 100644 --- a/vite/src/main.tsx +++ b/vite/src/main.tsx @@ -1,11 +1,10 @@ +import "./index.css"; +import App from "./App"; + import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; -import "./index.css"; import { ClerkProvider } from "@clerk/clerk-react"; -import App from "./App"; -import posthog from "posthog-js"; import { PostHogProvider } from "posthog-js/react"; -import { AutumnProvider } from "autumn-js/react"; const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY; if (!PUBLISHABLE_KEY) { diff --git a/vite/src/services/useAxiosSwr.tsx b/vite/src/services/useAxiosSwr.tsx index ea173b4c0..e2c2ba8e5 100644 --- a/vite/src/services/useAxiosSwr.tsx +++ b/vite/src/services/useAxiosSwr.tsx @@ -11,12 +11,12 @@ export function useAxiosSWR({ enabled = true, }: { url: string; - env: AppEnv; + env?: AppEnv; withAuth?: boolean; options?: SWRConfiguration; enabled?: boolean; }) { - const axiosInstance = useAxiosInstance({ env, isAuth: withAuth }); + const axiosInstance = useAxiosInstance({ isAuth: withAuth }); const fetcher = async (url: string) => { const res = await axiosInstance.get(url); diff --git a/vite/src/utils/formatUtils/formatTextUtils.ts b/vite/src/utils/formatUtils/formatTextUtils.ts index be1197b79..c40fb7d06 100644 --- a/vite/src/utils/formatUtils/formatTextUtils.ts +++ b/vite/src/utils/formatUtils/formatTextUtils.ts @@ -11,10 +11,13 @@ export const keyToTitleFirstCaps = (key: string) => { return res.replace(/_/g, " "); }; -export const slugify = (text: string) => { +export const slugify = ( + text: string, + type: "underscore" | "dash" = "underscore", +) => { return text .toLowerCase() - .replace(/ /g, "_") + .replace(/ /g, type == "underscore" ? "_" : "-") .replace(/[^\w\s-]/g, ""); }; diff --git a/vite/src/views/DefaultView.tsx b/vite/src/views/DefaultView.tsx index 09b9e414a..46731a928 100644 --- a/vite/src/views/DefaultView.tsx +++ b/vite/src/views/DefaultView.tsx @@ -1,5 +1,3 @@ -import { RedirectToSignIn, useOrganization, useUser } from "@clerk/clerk-react"; -import LoadingScreen from "./general/LoadingScreen"; import { Link, Navigate, useLocation } from "react-router"; import ErrorScreen from "./general/ErrorScreen"; @@ -18,11 +16,4 @@ export const DefaultView = () => { ); - - // By default, will come here - // 2. if user, redirect to customers - // return ; - // if (!user) { - // return
Hello World
; - // } }; diff --git a/vite/src/views/auth/SignIn.tsx b/vite/src/views/auth/SignIn.tsx index b404eb706..37328e5ce 100644 --- a/vite/src/views/auth/SignIn.tsx +++ b/vite/src/views/auth/SignIn.tsx @@ -8,6 +8,7 @@ import { signIn } from "@/lib/auth-client"; export const SignIn = () => { const [email, setEmail] = useState(""); + const [googleLoading, setGoogleLoading] = useState(false); const handleEmailSignIn = (e: React.FormEvent) => { e.preventDefault(); @@ -17,10 +18,19 @@ export const SignIn = () => { }; const handleGoogleSignIn = async () => { - await signIn.social({ - provider: "google", - callbackURL: "http://localhost:3000/customers", - }); + setGoogleLoading(true); + try { + await signIn.social({ + provider: "google", + callbackURL: "http://localhost:3000/customers", + }); + } catch (error) { + console.error("Error signing in with Google:", error); + } finally { + setTimeout(() => { + setGoogleLoading(false); + }, 1000); + } }; return ( @@ -45,8 +55,12 @@ export const SignIn = () => { variant="outline" onClick={handleGoogleSignIn} className="w-full h-10 font-medium text-sm gap-2" + // disabled={googleLoading} + isLoading={googleLoading} + startIcon={ + + } > - Continue with Google diff --git a/vite/src/views/customers/customer/add-product/NewProductDropdown.tsx b/vite/src/views/customers/customer/add-product/NewProductDropdown.tsx index 31881b051..23003c0fe 100644 --- a/vite/src/views/customers/customer/add-product/NewProductDropdown.tsx +++ b/vite/src/views/customers/customer/add-product/NewProductDropdown.tsx @@ -19,6 +19,7 @@ import { OrgService } from "@/services/OrgService"; import { CusProductStatus, Entity, Product } from "@autumn/shared"; import SmallSpinner from "@/components/general/SmallSpinner"; import { Search } from "lucide-react"; +import { useOrg } from "@/hooks/useOrg"; function AddProduct() { const { products, customer, env, entityId, entities } = useCustomerContext(); @@ -26,6 +27,7 @@ function AddProduct() { const [options, setOptions] = useState([]); const [searchQuery, setSearchQuery] = useState(""); const [open, setOpen] = useState(false); + const { org } = useOrg(); const filteredProducts = products.filter((product: Product) => { if (product.is_add_on && !searchQuery) return true; diff --git a/vite/src/views/main-sidebar/SidebarTop.tsx b/vite/src/views/main-sidebar/SidebarTop.tsx index a9e93e9a6..99640908f 100644 --- a/vite/src/views/main-sidebar/SidebarTop.tsx +++ b/vite/src/views/main-sidebar/SidebarTop.tsx @@ -19,34 +19,36 @@ import { } from "@/components/ui/tooltip"; import { Check, ChevronLeft, ChevronRight, Copy } from "lucide-react"; import { AdminHover } from "@/components/general/AdminHover"; +import { OrgDropdown } from "./components/OrgDropdown"; export const SidebarTop = () => { const { isLoaded, user } = useUser(); const { state, setState } = useSidebarContext(); const primaryEmail = user?.primaryEmailAddress?.emailAddress; const env = useEnv(); - const { organization } = useOrganization(); - const prevOrgIdRef = useRef(null); + // const { organization } = useOrganization(); + // const prevOrgIdRef = useRef(null); - useEffect(() => { - // Skip the first render - if (prevOrgIdRef.current === null) { - prevOrgIdRef.current = organization?.id || null; - return; - } + // useEffect(() => { + // // Skip the first render + // if (prevOrgIdRef.current === null) { + // prevOrgIdRef.current = organization?.id || null; + // return; + // } - // If organization changed (switched or created/deleted) - if (prevOrgIdRef.current !== (organization?.id || null)) { - console.log("Organization changed, refreshing page"); - window.location.reload(); - } + // // If organization changed (switched or created/deleted) + // if (prevOrgIdRef.current !== (organization?.id || null)) { + // console.log("Organization changed, refreshing page"); + // window.location.reload(); + // } - // Update the ref - prevOrgIdRef.current = organization?.id || null; - }, [organization]); + // // Update the ref + // prevOrgIdRef.current = organization?.id || null; + // }, [organization]); return (
+
{ {state == "expanded" && (
- {organization && ( + + {/* {organization && ( { }, ]} > - + - )} + )} */}
)} diff --git a/vite/src/views/main-sidebar/components/CreateNewOrg.tsx b/vite/src/views/main-sidebar/components/CreateNewOrg.tsx new file mode 100644 index 000000000..bfc835232 --- /dev/null +++ b/vite/src/views/main-sidebar/components/CreateNewOrg.tsx @@ -0,0 +1,104 @@ +import FieldLabel from "@/components/general/modal-components/FieldLabel"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { useOrg } from "@/hooks/useOrg"; +import { authClient } from "@/lib/auth-client"; +import { slugify } from "@/utils/formatUtils/formatTextUtils"; +import { useState } from "react"; +import { toast } from "sonner"; + +export const CreateNewOrg = ({ + dialogType, + setDialogType, +}: { + dialogType: "create" | "manage" | null; + setDialogType: (dialogType: "create" | "manage" | null) => void; +}) => { + const { mutate } = useOrg(); + const [name, setName] = useState(""); + const [slugChanged, setSlugChanged] = useState(false); + const [slug, setSlug] = useState(""); + const [isLoading, setIsLoading] = useState(false); + + const handleCreate = async () => { + setIsLoading(true); + try { + const { data, error } = await authClient.organization.create({ + name, + slug, + }); + + if (error) throw error; + + await authClient.organization.setActive({ + organizationId: data.id, + }); + + await mutate(); + + toast.success("Organization created"); + setDialogType(null); + } catch (error: any) { + console.log(error); + toast.error(error.message); + } finally { + setIsLoading(false); + } + }; + + return ( + { + if (!open) setDialogType(null); + }} + > + + +
+ + Create New Organization + +
+
+ Name + { + setName(e.target.value); + if (!slugChanged) { + setSlug(slugify(e.target.value)); + } + }} + /> +
+
+ Slug + { + if (!slugChanged) { + setSlug(slugify(e.target.value)); + } + }} + /> +
+
+
+ + + +
+
+ ); +}; diff --git a/vite/src/views/main-sidebar/components/LogOutItem.tsx b/vite/src/views/main-sidebar/components/LogOutItem.tsx new file mode 100644 index 000000000..8f47f28b4 --- /dev/null +++ b/vite/src/views/main-sidebar/components/LogOutItem.tsx @@ -0,0 +1,29 @@ +import { authClient } from "@/lib/auth-client"; +import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; +import { LogOut } from "lucide-react"; +import { useState } from "react"; + +export const LogOutItem = () => { + const [loading, setLoading] = useState(false); + + return ( + { + try { + setLoading(true); + await authClient.signOut(); + window.location.reload(); + } catch (error) { + console.error("Error signing out:", error); + } finally { + setLoading(false); + } + }} + > +
+ Log Out + +
+
+ ); +}; diff --git a/vite/src/views/main-sidebar/components/OrgDropdown.tsx b/vite/src/views/main-sidebar/components/OrgDropdown.tsx new file mode 100644 index 000000000..e5bf540cd --- /dev/null +++ b/vite/src/views/main-sidebar/components/OrgDropdown.tsx @@ -0,0 +1,158 @@ +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuPortal, + DropdownMenuSeparator, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { useOrg } from "@/hooks/useOrg"; +import { + authClient, + useListOrganizations, + useSession, +} from "@/lib/auth-client"; +import { FrontendOrg } from "@autumn/shared"; +import { DropdownMenuGroup } from "@radix-ui/react-dropdown-menu"; +import { LogOut, Plus, Settings, Trash } from "lucide-react"; +import React from "react"; +import { useState } from "react"; +import { CreateNewOrg } from "./CreateNewOrg"; +import { toast } from "sonner"; +import { LogOutItem } from "./LogOutItem"; +import { cn } from "@/lib/utils"; + +const OrgLogo = ({ org }: { org: FrontendOrg }) => { + const firstLetter = org.name.charAt(0).toUpperCase(); + return ( +
+ {org.logo ? ( + {org.name} + ) : ( + {firstLetter} + )} +
+ ); +}; + +export const OrgDropdown = () => { + const { org, isLoading } = useOrg(); + const { data: orgs, isPending } = useListOrganizations(); + const [dialogType, setDialogType] = useState<"create" | "manage" | null>( + null, + ); + + const [dropdownOpen, setDropdownOpen] = useState(false); + + if (!org) return null; + if (isLoading) return
; + + return ( + + + + + + + + + + +
+ Manage + +
+
+ { + setDialogType("create"); + }} + > +
+ Create New + +
+
+ + + + + Switch Organization + + + + {orgs?.map((org) => ( + + ))} + + + +
+ + +
+
+
+ ); +}; + +const SwitchOrgItem = ({ org, setDropdownOpen }: any) => { + const [loading, setLoading] = useState(false); + + const { mutate } = useOrg(); + + const handleSwitchOrg = async (orgId: string) => { + setLoading(true); + + try { + await authClient.organization.setActive({ + organizationId: orgId, + }); + + window.location.reload(); + } catch (error: any) { + toast.error(error.message); + } finally { + setLoading(false); + } + }; + + return ( + { + e.preventDefault(); + await handleSwitchOrg(org.id); + setDropdownOpen(false); + }} + shimmer={loading} + className="flex justify-between" + > + {org.name} + + ); +}; diff --git a/vite/src/views/onboarding/OnboardingView.tsx b/vite/src/views/onboarding/OnboardingView.tsx index d786359d5..973238ac9 100644 --- a/vite/src/views/onboarding/OnboardingView.tsx +++ b/vite/src/views/onboarding/OnboardingView.tsx @@ -1,7 +1,6 @@ "use client"; import { useState, useEffect, useRef } from "react"; -import { useAuth, useOrganization } from "@clerk/clerk-react"; import { useSearchParams } from "react-router"; import Step from "@/components/general/OnboardingStep"; import { useAxiosSWR } from "@/services/useAxiosSwr"; @@ -22,21 +21,19 @@ import { SampleApp } from "./onboarding-steps/SampleApp"; import IntegrationGuideStep from "./onboarding-steps/IntegrationGuide"; import AutumnProviderStep from "./onboarding-steps/AutumnProvider"; import { AutumnProvider } from "autumn-js/react"; +import { useSession } from "@/lib/auth-client"; function OnboardingView() { const env = useEnv(); - // const { organization: org } = useOrganization(); const [searchParams] = useSearchParams(); - + const token = searchParams.get("token"); const [apiKey, setApiKey] = useState(""); const [showIntegrationSteps, setShowIntegrationSteps] = useState(false); - - const hasHandledToken = useRef(false); - const axiosInstance = useAxiosInstance(); - const token = searchParams.get("token"); const [loading, setLoading] = useState(true); - const { getToken } = useAuth(); + const { data } = useSession(); + + const orgId = data?.session?.activeOrganizationId; const { data: productData, @@ -71,11 +68,11 @@ function OnboardingView() { // } // }, [org, searchParams, token, axiosInstance, productMutate]); - // useEffect(() => { - // if (org && !token) { - // setLoading(false); - // } - // }, [org, token]); + useEffect(() => { + if (orgId && !token) { + setLoading(false); + } + }, [orgId, token]); if (loading || productLoading) { return ; @@ -94,13 +91,12 @@ function OnboardingView() { /> { - const token = await getToken({ - template: "custom_template", - }); - return token; - }} + // getBearerToken={async () => { + // const token = await getToken({ + // template: "custom_template", + // }); + // return token; + // }} > diff --git a/vite/src/views/onboarding/hooks/useCreateOrg.tsx b/vite/src/views/onboarding/hooks/useCreateOrg.tsx index 7cebd43ee..dfa221f01 100644 --- a/vite/src/views/onboarding/hooks/useCreateOrg.tsx +++ b/vite/src/views/onboarding/hooks/useCreateOrg.tsx @@ -1,6 +1,10 @@ -import { useSession } from "@/lib/auth-client"; +import { + authClient, + useListOrganizations, + useSession, +} from "@/lib/auth-client"; import { useAxiosInstance } from "@/services/useAxiosInstance"; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef } from "react"; export const useCreateOrg = ({ productMutate, @@ -11,18 +15,28 @@ export const useCreateOrg = ({ const hasCreatedOrg = useRef(false); const { data: session } = useSession(); - // const { user } = useUser(); - // const { organization: org } = useOrganization(); - // const { setActive } = useOrganizationList(); + const { data: organizations, isPending } = useListOrganizations(); useEffect(() => { - console.log("session", session); const createDefaultOrg = async () => { - if (hasCreatedOrg.current) return; + if (hasCreatedOrg.current || isPending) return; hasCreatedOrg.current = true; // Either set first org active, or create a new org try { - await axiosInstance.post("/organization"); + if (organizations && organizations.length > 0) { + await authClient.organization.setActive({ + organizationId: organizations[0]?.id, + }); + } else { + await authClient.organization.create({ + name: `${session?.user.name}'s Org`, + slug: crypto.randomUUID(), + }); + + await productMutate(); + } + // await axiosInstance.post("/organization"); + // await authClient.use // if ( // user?.organizationMemberships?.length && // user.organizationMemberships.length > 0 @@ -30,7 +44,7 @@ export const useCreateOrg = ({ // await setActive?.({ // organization: user.organizationMemberships[0].organization.id, // }); - // await productMutate(); + // return; // } else { // const { data } = await axiosInstance.post("/organization"); diff --git a/vite/src/views/onboarding/onboarding-steps/CreateOrg.tsx b/vite/src/views/onboarding/onboarding-steps/CreateOrg.tsx index ef7cb8a3b..25dfa312d 100644 --- a/vite/src/views/onboarding/onboarding-steps/CreateOrg.tsx +++ b/vite/src/views/onboarding/onboarding-steps/CreateOrg.tsx @@ -3,125 +3,122 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { useAxiosInstance } from "@/services/useAxiosInstance"; import { useEnv } from "@/utils/envUtils"; -import { useOrganization, useOrganizationList } from "@clerk/clerk-react"; -import { Building } from "lucide-react"; import { useState } from "react"; import ConfettiExplosion from "react-confetti-explosion"; import { toast } from "sonner"; -export const CreateOrgStep = ({ - number, - pollForOrg, -}: { - number: number; - pollForOrg: () => Promise; -}) => { - const { organization: org } = useOrganization(); - const env = useEnv(); - const axios = useAxiosInstance({ env }); - const { createOrganization, setActive } = useOrganizationList(); +// export const CreateOrgStep = ({ +// number, +// pollForOrg, +// }: { +// number: number; +// pollForOrg: () => Promise; +// }) => { - const [isExploding, setIsExploding] = useState(false); - const [loading, setLoading] = useState(false); - const [fields, setFields] = useState({ - name: org?.name || "", - slug: "", - }); +// const env = useEnv(); +// const axios = useAxiosInstance({ env }); - const handleCreateOrg = async () => { - setLoading(true); +// const [isExploding, setIsExploding] = useState(false); +// const [loading, setLoading] = useState(false); +// const [fields, setFields] = useState({ +// name: org?.name || "", +// slug: "", +// }); - try { - if (!createOrganization) { - toast.error("Error creating organization"); - return; - } +// const handleCreateOrg = async () => { +// setLoading(true); - const org = await createOrganization({ - name: fields.name, - }); +// try { +// if (!createOrganization) { +// toast.error("Error creating organization"); +// return; +// } - // Create org in Autumn - const res = await axios.post("/organization", { - orgId: org.id, - }); +// const org = await createOrganization({ +// name: fields.name, +// }); - console.log("Org created in Autumn", res); +// // Create org in Autumn +// const res = await axios.post("/organization", { +// orgId: org.id, +// }); - await setActive({ organization: org.id }); - // await pollForOrg(); - toast.success(`Created your organization: ${org.name}`); - setIsExploding(true); - } catch (error: any) { - if (error.message) { - toast.error(error.message); - } else { - toast.error("Error creating organization"); - } - } - setLoading(false); - }; +// console.log("Org created in Autumn", res); - return ( - -
-
-

- 👋 - -   Welcome to Autumn - -

-
-

- Create an organization to get started and integrate pricing within 5 - minutes. -

- - } - > - {/*
*/} -
- { - const newFields = { ...fields, name: e.target.value }; - setFields(newFields); - }} - /> - +// await setActive({ organization: org.id }); +// // await pollForOrg(); +// toast.success(`Created your organization: ${org.name}`); +// setIsExploding(true); +// } catch (error: any) { +// if (error.message) { +// toast.error(error.message); +// } else { +// toast.error("Error creating organization"); +// } +// } +// setLoading(false); +// }; - {isExploding && ( - { - console.log("complete"); - }} - /> - )} -
- {/*
*/} -
- ); -}; +// return ( +// +//
+//
+//

+// 👋 +// +//   Welcome to Autumn +// +//

+//
+//

+// Create an organization to get started and integrate pricing within 5 +// minutes. +//

+// +// } +// > +// {/*
*/} +//
+// { +// const newFields = { ...fields, name: e.target.value }; +// setFields(newFields); +// }} +// /> +// + +// {isExploding && ( +// { +// console.log("complete"); +// }} +// /> +// )} +//
+// {/*
*/} +//
+// ); +// };