diff --git a/vite/src/views/onboarding2/integrate/SelectStack.tsx b/vite/src/views/onboarding2/integrate/SelectStack.tsx
index a28cff3e2..77c8398d5 100644
--- a/vite/src/views/onboarding2/integrate/SelectStack.tsx
+++ b/vite/src/views/onboarding2/integrate/SelectStack.tsx
@@ -8,176 +8,78 @@ import {
SelectValue,
} from "@/components/ui/select";
import { useIntegrateContext } from "./IntegrateContext";
+import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import {
+ Building,
+ CircleUserRound,
+ Code,
+ Fingerprint,
+ Info,
+ InfoIcon,
+ ScanFace,
+ User,
+} from "lucide-react";
+import { SelectFrameworks } from "./select-stack/SelectFrameworks";
+import { InfoBox } from "./components/InfoBox";
+import { Button } from "@/components/ui/button";
export const SelectStack = () => {
const { queryStates, setQueryStates } = useIntegrateContext();
- const frontendOptions = [
- { value: "nextjs", label: "Next.js" },
- { value: "vite", label: "Vite SPA" },
- { value: "tanstack", label: "Tanstack Start" },
- { value: "non-react", label: "Non-React" },
- ];
-
- const backendOptions = [
- { value: "nextjs", label: "Next.js" },
- { value: "react_router", label: "React Router 7" },
- { value: "hono", label: "Hono" },
- { value: "express", label: "Express" },
- { value: "elysia", label: "Elysia" },
- { value: "supabase", label: "Supabase" },
- { value: "convex", label: "Convex" },
- ];
-
- const authOptions = [
- { value: "better_auth", label: "Better Auth" },
- { value: "supabase", label: "Supabase Auth" },
- { value: "clerk", label: "Clerk" },
- { value: "other", label: "Other" },
- ];
-
- const customerOptions = [
- { value: "user", label: "Users" },
- { value: "org", label: "Organizations" },
- // { value: "other", label: "Other (eg. Projects / Workspaces)" },
- ];
-
+ const tabClassName = `rounded-xs h-8 data-[state=active]:bg-stone-100 data-[state=active]:text-t2 data-[state=active]:shadow-inner data-[state=active]:border`;
return (
-
-
- Help us customize the integration guide for your specific tech stack.
-
-
-
- {/* Frontend Framework */}
-
-
Frontend
-
- setQueryStates({ ...queryStates, frontend: value })
- }
+
+
+
+ Help us customize the integration guide for your specific tech stack.
+ Click{" "}
+ {
+ setQueryStates({ ...queryStates, reactTypescript: false });
+ }}
+ className="underline cursor-pointer"
>
-
-
-
-
- {frontendOptions.map((option) => (
-
- {option.label}
-
- ))}
-
-
-
-
- {/* Backend Framework */}
-
- Backend
-
- setQueryStates({ ...queryStates, backend: value })
- }
- >
-
-
-
-
- {backendOptions.map((option) => (
-
- {option.label}
-
- ))}
-
-
-
-
- {/* Auth Provider */}
-
- Auth Provider
-
- setQueryStates({ ...queryStates, auth: value })
- }
- >
-
-
-
-
- {authOptions.map((option) => (
-
- {option.label}
-
- ))}
-
-
-
-
- {/* Customer Type */}
-
-
- Your customers are
-
-
- setQueryStates({ ...queryStates, customerType: value })
- }
- >
-
-
-
-
- {customerOptions.map((option) => (
-
- {option.label}
-
- ))}
-
-
-
+ here
+ {" "}
+ if you're not using a React + Typescript backend stack.
+
-
- {/* {(stack.frontend ||
- stack.backend ||
- stack.auth ||
- stack.customerType) && (
-
-
Selected Stack:
-
- {stack.frontend && (
-
- Frontend: {" "}
- {frontendOptions.find((f) => f.value === stack.frontend)?.label}
-
- )}
- {stack.backend && (
-
- Backend: {" "}
- {backendOptions.find((b) => b.value === stack.backend)?.label}
-
- )}
- {stack.auth && (
-
- Auth: {" "}
- {authOptions.find((a) => a.value === stack.auth)?.label}
-
- )}
- {stack.customerType && (
-
- Customers: {" "}
- {
- customerOptions.find((c) => c.value === stack.customerType)
- ?.label
- }
-
- )}
-
-
- )} */}
+ {queryStates.reactTypescript ? (
+
+ ) : (
+ <>
+
+
+ This onboarding guide shows how to set up Autumn using our
+ frontend components / hooks on React and server-side framework
+ adaptors.
+
+
+ In your case, you should integrate with Autumn's API directly on
+ the backend. We have SDKs for Typescript and Python. Learn how to
+ do so{" "}
+
+ here
+
+ .
+
+
+
{
+ setQueryStates({ ...queryStates, reactTypescript: true });
+ }}
+ >
+ Learn how to integrate Autumn with React + Typescript
+
+ >
+ )}
);
};
diff --git a/vite/src/views/onboarding2/integrate/StackEnums.tsx b/vite/src/views/onboarding2/integrate/StackEnums.tsx
new file mode 100644
index 000000000..243a5317b
--- /dev/null
+++ b/vite/src/views/onboarding2/integrate/StackEnums.tsx
@@ -0,0 +1,15 @@
+export enum Backend {
+ Nextjs = "nextjs",
+ ReactRouter = "react-router",
+ Express = "express",
+ Elysia = "elysia",
+ Hono = "hono",
+ Other = "other",
+}
+
+export enum Frontend {
+ Nextjs = "nextjs",
+ ReactRouter = "react-router",
+ Vite = "vite",
+ Other = "other",
+}
diff --git a/vite/src/views/onboarding2/integrate/StepHeader.tsx b/vite/src/views/onboarding2/integrate/StepHeader.tsx
index 8ac04206d..79751e60b 100644
--- a/vite/src/views/onboarding2/integrate/StepHeader.tsx
+++ b/vite/src/views/onboarding2/integrate/StepHeader.tsx
@@ -3,14 +3,14 @@ export const StepHeader = ({
title,
}: {
number: number;
- title: string;
+ title: React.ReactNode;
}) => {
return (
{number}
-
{title}
+
{title}
);
};
diff --git a/vite/src/views/onboarding2/integrate/components/CodeSpan.tsx b/vite/src/views/onboarding2/integrate/components/CodeSpan.tsx
new file mode 100644
index 000000000..ffbc6ef17
--- /dev/null
+++ b/vite/src/views/onboarding2/integrate/components/CodeSpan.tsx
@@ -0,0 +1,7 @@
+export const CodeSpan = ({ children }: { children: React.ReactNode }) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/vite/src/views/onboarding2/integrate/components/InfoBox.tsx b/vite/src/views/onboarding2/integrate/components/InfoBox.tsx
new file mode 100644
index 000000000..6462880a6
--- /dev/null
+++ b/vite/src/views/onboarding2/integrate/components/InfoBox.tsx
@@ -0,0 +1,27 @@
+import { cn } from "@/lib/utils";
+import { Info } from "lucide-react";
+
+export const InfoBox = ({
+ classNames,
+ children,
+}: {
+ classNames?: {
+ infoIcon?: string;
+ infoBox?: string;
+ };
+ children: React.ReactNode;
+}) => {
+ return (
+
+ );
+};
diff --git a/vite/src/views/onboarding2/integrate/integration-steps/AddAutumnProvider.tsx b/vite/src/views/onboarding2/integrate/integration-steps/AddAutumnProvider.tsx
new file mode 100644
index 000000000..8026fdbad
--- /dev/null
+++ b/vite/src/views/onboarding2/integrate/integration-steps/AddAutumnProvider.tsx
@@ -0,0 +1,162 @@
+import CodeBlock from "@/views/onboarding/components/CodeBlock";
+import { CodeSpan } from "../components/CodeSpan";
+import { StepHeader } from "../StepHeader";
+import { useIntegrateContext } from "../IntegrateContext";
+import { Backend, Frontend } from "../StackEnums";
+import { InfoBox } from "../components/InfoBox";
+
+const nextjsAutumnProvider = ({
+ includeBackendUrl,
+}: {
+ includeBackendUrl: boolean;
+}) => {
+ const backendUrlStr = includeBackendUrl
+ ? ` backendUrl={process.env.NEXT_PUBLIC_BACKEND_URL}`
+ : "";
+ return `// layout.tsx
+
+import { AutumnProvider, PricingTable } from "autumn-js/react";
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+
+ {children}
+
+
+
+ );
+}`;
+};
+
+const rr7AutumnProvider = ({
+ includeBackendUrl,
+}: {
+ includeBackendUrl: boolean;
+}) => {
+ const backendUrlStr = includeBackendUrl
+ ? ` backendUrl={import.meta.env.VITE_BACKEND_URL}`
+ : "";
+ return `// root.tsx
+
+import { AutumnProvider } from "autumn-js/react";
+export function Layout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+
+
+
+
+
+
+
{children}
+
+
+
+
+ );
+}`;
+};
+
+const viteAutumnProvider = () => {
+ return `// main.tsx
+import { AutumnProvider } from "autumn-js/react";
+
+createRoot(document.getElementById("root")!).render(
+
+
+
+);`;
+};
+
+const otherAutumnProvider = () => {
+ return `// main.tsx
+import { AutumnProvider } from "autumn-js/react";
+
+// 1. Simply wrap the root of your app in the AutumnProvider component
+// 2. Pass in your server's URL to the backendUrl prop
+
+createRoot(document.getElementById("root")!).render(
+
+
+
+
+
+);`;
+};
+
+const getSnippet = (queryStates: any) => {
+ const { backend, frontend } = queryStates;
+ if (frontend === Frontend.Nextjs) {
+ return nextjsAutumnProvider({
+ includeBackendUrl: backend !== Backend.Nextjs,
+ });
+ } else if (frontend === Frontend.ReactRouter) {
+ return rr7AutumnProvider({
+ includeBackendUrl: backend === Backend.Nextjs,
+ });
+ } else if (frontend === Frontend.Vite) {
+ return viteAutumnProvider();
+ } else {
+ return otherAutumnProvider();
+ }
+};
+
+export const AddAutumnProvider = () => {
+ const { queryStates } = useIntegrateContext();
+ return (
+
+
+ Wrap your React app in {" "}
+
+ }
+ />
+
+ This allows you to use our React hooks and components in your app. If
+ your server URL is different to your client, you will need to pass in
+ the backend URL as a prop.
+
+
+ {queryStates.auth === "supabase" && (
+
+
+ The examples above assume that Supabase auth is implemented using
+ server-side cookie authentication, following the guide{" "}
+
+ here
+
+ .
+
+
+ If you need to set {"Bearer "} in
+ your request headers to authenticate, you can use the{" "}
+ getBearerToken prop in the{" "}
+ AutumnProvider component.
+
+
+ )}
+
+ );
+};
diff --git a/vite/src/views/onboarding2/integrate/integration-steps/AutumnHandler.tsx b/vite/src/views/onboarding2/integrate/integration-steps/AutumnHandler.tsx
index 6346ba3fb..a3b34292b 100644
--- a/vite/src/views/onboarding2/integrate/integration-steps/AutumnHandler.tsx
+++ b/vite/src/views/onboarding2/integrate/integration-steps/AutumnHandler.tsx
@@ -12,22 +12,36 @@ import {
nextjsSupabaseOrg,
nextjsSupabaseUser,
} from "./snippets/nextjsHandler";
-import { rr7BetterAuth, rr7Clerk, rr7Supabase } from "./snippets/rr7Handler";
+import {
+ rr7BetterAuth,
+ rr7Clerk,
+ rr7Other,
+ rr7Supabase,
+} from "./snippets/rr7Handler";
import {
honoBetterAuth,
honoClerk,
honoSupabase,
+ honoOther,
} from "./snippets/honoHandler";
import {
expressBetterAuth,
expressClerk,
+ expressOther,
expressSupabase,
} from "./snippets/expressHandler";
-import { elysiaBetterAuth } from "./snippets/elysiaHandler";
+import {
+ elysiaBetterAuth,
+ elysiaClerk,
+ elysiaOther,
+} from "./snippets/elysiaHandler";
+import { general } from "./snippets/general";
+import { CodeSpan } from "../components/CodeSpan";
+import { Backend } from "../StackEnums";
const snippet = () => {
return {
- nextjs: {
+ [Backend.Nextjs]: {
["better_auth"]: {
user: nextjsBetterAuthUser,
org: nextjsBetterAuthOrg,
@@ -45,7 +59,7 @@ const snippet = () => {
org: nextjsOther,
},
},
- ["react_router"]: {
+ [Backend.ReactRouter]: {
["better_auth"]: {
user: rr7BetterAuth("user"),
org: rr7BetterAuth("org"),
@@ -58,8 +72,12 @@ const snippet = () => {
user: rr7Clerk("user"),
org: rr7Clerk("org"),
},
+ ["other"]: {
+ user: rr7Other("user"),
+ org: rr7Other("org"),
+ },
},
- ["hono"]: {
+ [Backend.Hono]: {
["clerk"]: {
user: honoClerk("user"),
org: honoClerk("org"),
@@ -72,8 +90,12 @@ const snippet = () => {
user: honoBetterAuth("user"),
org: honoBetterAuth("org"),
},
+ ["other"]: {
+ user: honoOther("user"),
+ org: honoOther("org"),
+ },
},
- ["express"]: {
+ [Backend.Express]: {
["better_auth"]: {
user: expressBetterAuth("user"),
org: expressBetterAuth("org"),
@@ -86,12 +108,24 @@ const snippet = () => {
user: expressSupabase("user"),
org: expressSupabase("org"),
},
+ ["other"]: {
+ user: expressOther("user"),
+ org: expressOther("org"),
+ },
},
- ["elysia"]: {
+ [Backend.Elysia]: {
["better_auth"]: {
user: elysiaBetterAuth("user"),
org: elysiaBetterAuth("org"),
},
+ ["clerk"]: {
+ user: elysiaClerk("user"),
+ org: elysiaClerk("org"),
+ },
+ ["other"]: {
+ user: elysiaOther("user"),
+ org: elysiaOther("org"),
+ },
},
} as any;
};
@@ -106,21 +140,25 @@ export const AutumnHandler = () => {
const templates = snippet();
- console.log("Backend Lang", backendLang);
- console.log("Auth Provider", authProvider);
+ let template = templates[backendLang]?.[authProvider]?.[customerType] || "";
- const template =
- templates[backendLang]?.[authProvider]?.[customerType] || "";
+ template = template.trim("\n");
- console.log("Template", template);
+ if (!template) {
+ return general();
+ }
- // Trim \n from the template (only left and right)
- return template.trim("\n");
+ return template;
};
return (
-
-
+
+
+
+ autumnHandler mounts routes on the{" "}
+ /api/autumn/* paths which allows our React hooks
+ and components to interact with the Autumn API directly.
+
{
+ return `import { PricingTable } from "autumn-js/react";
+
+export default function Home() {
+ return (
+
+ );
+}`;
+};
+
+const getSnippet = (queryStates: any) => {
+ return nextjsSnippet();
+};
+
+export const CheckoutPricingTable = () => {
+ const { queryStates } = useIntegrateContext();
+ return (
+
+
+ Drop in {" "}
+
+ }
+ />
+
+ Display a pricing table with the plans you have created and let your
+ customers choose a plan.
+
+
+
+
+ {
+ "Our component is completely customisable by installing it as a shadcn component. "
+ }
+ Learn how to do so{" "}
+
+ here
+
+ .
+
+
+
+ );
+};
diff --git a/vite/src/views/onboarding2/integrate/integration-steps/CreateSecretKey.tsx b/vite/src/views/onboarding2/integrate/integration-steps/CreateSecretKey.tsx
new file mode 100644
index 000000000..dc22a6e12
--- /dev/null
+++ b/vite/src/views/onboarding2/integrate/integration-steps/CreateSecretKey.tsx
@@ -0,0 +1,114 @@
+import { Input } from "@/components/ui/input";
+
+import { DevContext } from "@/views/developer/DevContext";
+
+import Step from "@/components/general/OnboardingStep";
+
+import { useEnv } from "@/utils/envUtils";
+import { useState } from "react";
+import { toast } from "sonner";
+import { DevService } from "@/services/DevService";
+import { useAxiosInstance } from "@/services/useAxiosInstance";
+import { Button } from "@/components/ui/button";
+import { CheckIcon, CopyIcon, PlusIcon } from "lucide-react";
+
+export const CreateSecretKey = ({
+ apiKey,
+ setApiKey,
+}: {
+ apiKey: string;
+ setApiKey: (apiKey: string) => void;
+}) => {
+ const env = useEnv();
+ // const [apiKeyName, setApiKeyName] = useState("");
+ const [apiCreated, setApiCreated] = useState(false);
+
+ const [loading, setLoading] = useState(false);
+ const [copied, setCopied] = useState(false);
+ const axiosInstance = useAxiosInstance({ env });
+
+ const handleCreate = async () => {
+ setLoading(true);
+ try {
+ const { api_key } = await DevService.createAPIKey(axiosInstance, {
+ name: "Autumn Onboarding",
+ });
+
+ setApiKey(api_key);
+ } catch (error) {
+ console.log("Error:", error);
+ toast.error("Failed to create API key");
+ }
+
+ setLoading(false);
+ };
+
+ return (
+ {},
+ onboarding: true,
+ apiCreated,
+ setApiCreated,
+ }}
+ >
+
+
+ );
+};
+
+{
+ /*
+ {env === AppEnv.Sandbox ? (
+
+ ) : (
+
+ )}
+
*/
+}
diff --git a/vite/src/views/onboarding2/integrate/integration-steps/EnvStep.tsx b/vite/src/views/onboarding2/integrate/integration-steps/EnvStep.tsx
new file mode 100644
index 000000000..28e682ca8
--- /dev/null
+++ b/vite/src/views/onboarding2/integrate/integration-steps/EnvStep.tsx
@@ -0,0 +1,36 @@
+import CodeBlock from "@/views/onboarding/components/CodeBlock";
+import { CodeSpan } from "../components/CodeSpan";
+import { StepHeader } from "../StepHeader";
+import { useState } from "react";
+import { CreateSecretKey } from "./CreateSecretKey";
+
+export const EnvStep = () => {
+ const [apiKey, setApiKey] = useState("");
+ return (
+ <>
+
+
+ Add the Autumn secret key to your {".env"} {" "}
+ file
+
+ }
+ />
+
+
+
+
+ >
+ );
+};
diff --git a/vite/src/views/onboarding2/integrate/integration-steps/snippets/elysiaHandler.tsx b/vite/src/views/onboarding2/integrate/integration-steps/snippets/elysiaHandler.tsx
index 8b1862693..9c99bb277 100644
--- a/vite/src/views/onboarding2/integrate/integration-steps/snippets/elysiaHandler.tsx
+++ b/vite/src/views/onboarding2/integrate/integration-steps/snippets/elysiaHandler.tsx
@@ -4,7 +4,7 @@ export const elysiaBetterAuth = (customerType: "user" | "org") => {
return `import { autumnHandler } from "autumn-js/elysia";
import { auth } from "./auth";
-const app = new Elysia({ adapter: node() })
+const app = new Elysia()
.use(cors())
.mount(auth.handler)
.use(
@@ -19,8 +19,60 @@ ${betterAuthSnippet(customerType, "context.headers", 4)}
`;
};
-// export const elysiaOther = (customerType: "user" | "org") => {
-// return `
+export const elysiaClerk = (customerType: "user" | "org") => {
+ return `import { clerkPlugin } from "elysia-clerk";
+import { autumnHandler } from "autumn-js/backend";
-// `;
-// };
+const app = new Elysia()
+ .use(cors())
+ .use(clerkPlugin())
+ .all("*", async (ctx: any) => {
+ console.log("Request received");
+ })
+ .all("/api/autumn/*", async (ctx: any) => {
+ const auth = ctx.auth();
+
+ let body = null;
+ if (ctx.request.method !== "GET") {
+ body = await ctx.request.json();
+ }
+
+ const { statusCode, response } = await autumnHandler({
+ customerId: ${customerType === "user" ? "auth.userId" : "auth.orgId"},
+ customerData: { name: "", email: "" },
+ request: {
+ url: ctx.request.url,
+ method: ctx.request.method,
+ body: body,
+ },
+ });
+
+ ctx.set.status = statusCode;
+ return response;
+ })
+ .listen(8000);`;
+};
+
+export const elysiaOther = (customerType: "user" | "org") => {
+ return `import { autumnHandler } from "autumn-js/elysia";
+import { auth } from "./auth";
+
+const app = new Elysia()
+ .use(cors())
+ .mount(auth.handler)
+ .use(
+ autumnHandler({
+ identify: async (context) => {
+ const customerId = "your_customer_id"; // Authenticate and get customer ID from your DB
+
+ return {
+ customerId,
+ customerData: { name: "", email: "" },
+ };
+ },
+ })
+ )
+ .listen(8000);
+
+ `;
+};
diff --git a/vite/src/views/onboarding2/integrate/integration-steps/snippets/expressHandler.tsx b/vite/src/views/onboarding2/integrate/integration-steps/snippets/expressHandler.tsx
index b251636f2..10c666549 100644
--- a/vite/src/views/onboarding2/integrate/integration-steps/snippets/expressHandler.tsx
+++ b/vite/src/views/onboarding2/integrate/integration-steps/snippets/expressHandler.tsx
@@ -46,3 +46,22 @@ app.use(
})
);`;
};
+
+export const expressOther = (customerType: "user" | "org") => {
+ return `import { autumnHandler } from "autumn-js/express";
+
+app.use(express.json()); // need to parse request body before autumnHandler
+app.use(
+ "/api/autumn",
+ autumnHandler({
+ identify: async (req, res) => {
+ const customerId = "your_customer_id"; // Get customer id from your database
+
+ return {
+ customerId,
+ customerData: { name: "", email: "" },
+ };
+ },
+ })
+);`;
+};
diff --git a/vite/src/views/onboarding2/integrate/integration-steps/snippets/general.tsx b/vite/src/views/onboarding2/integrate/integration-steps/snippets/general.tsx
new file mode 100644
index 000000000..e3da5a42c
--- /dev/null
+++ b/vite/src/views/onboarding2/integrate/integration-steps/snippets/general.tsx
@@ -0,0 +1,41 @@
+export const general = () => {
+ return `import { autumnHandler } from "autumn-js/backend";
+
+// 1. autumnHandler takes in request properties and returns a response
+// 2. Simply mount the handler onto the /api/autumn/* path in your backend
+// 3. Call autumnHandler and pass in the required parameters
+// 4. Return the response from the autumnHandler
+
+// Example using autumnHandler with Hono & Clerk
+import { autumnHandler } from "autumn-js/backend";
+import { clerkMiddleware, getAuth } from "@hono/clerk-auth";
+
+app.use("*", clerkMiddleware());
+app.use(
+ "/api/autumn/*",
+ async (c) => {
+ const auth = getAuth(c);
+
+ if (!auth?.userId) {
+ return c.json({ message: "Unauthorized" }, 401);
+ }
+
+ let body = null;
+ if (c.req.method !== "GET") {
+ body = await c.req.json();
+ }
+
+ const { statusCode, response } = await autumnHandler({
+ customerId: auth.userId,
+ customerData: { name: "", email: "" },
+ request: {
+ url: c.req.url,
+ method: c.req.method,
+ body: body,
+ },
+ });
+
+ return c.json(response, statusCode);
+ }
+);`;
+};
diff --git a/vite/src/views/onboarding2/integrate/integration-steps/snippets/honoHandler.tsx b/vite/src/views/onboarding2/integrate/integration-steps/snippets/honoHandler.tsx
index b323421a3..f052f0815 100644
--- a/vite/src/views/onboarding2/integrate/integration-steps/snippets/honoHandler.tsx
+++ b/vite/src/views/onboarding2/integrate/integration-steps/snippets/honoHandler.tsx
@@ -104,3 +104,23 @@ ${betterAuthSnippet(customerType, "c.req.raw.headers", 3)}
})
);`;
};
+export const honoOther = (customerType: "user" | "org") => {
+ return `// index.ts
+
+import { autumnHandler } from "autumn-js/hono";
+import { auth } from "@/lib/auth"
+
+app.use(
+ "/api/autumn/*",
+ autumnHandler({
+ identify: async (c: Context) => {
+ const customerId = "your_customer_id"; // Get customer id from your database
+
+ return {
+ customerId,
+ customerData: { name: "", email: "" },
+ };
+ },
+ })
+);`;
+};
diff --git a/vite/src/views/onboarding2/integrate/integration-steps/snippets/nextjsHandler.tsx b/vite/src/views/onboarding2/integrate/integration-steps/snippets/nextjsHandler.tsx
index 035cf0348..b295fd420 100644
--- a/vite/src/views/onboarding2/integrate/integration-steps/snippets/nextjsHandler.tsx
+++ b/vite/src/views/onboarding2/integrate/integration-steps/snippets/nextjsHandler.tsx
@@ -50,8 +50,8 @@ export const { GET, POST } = autumnHandler({
export const nextjsSupabaseUser = `
// app/api/autumn/[...all]/route.ts
-import { createClient } from "@/utils/supabase/server";
import { autumnHandler } from "autumn-js/next";
+import { createClient } from "@/utils/supabase/server";
export const { GET, POST } = autumnHandler({
identify: async () => {
@@ -75,8 +75,8 @@ export const { GET, POST } = autumnHandler({
export const nextjsSupabaseOrg = `
// app/api/autumn/[...all]/route.ts
-import { createClient } from "@/utils/supabase/server";
import { autumnHandler } from "autumn-js/next";
+import { createClient } from "@/utils/supabase/server";
export const { GET, POST } = autumnHandler({
identify: async () => {
@@ -150,13 +150,10 @@ import { autumnHandler } from "autumn-js/next";
export const { GET, POST } = autumnHandler({
identify: async (request) => {
-
- // Authenticate the request and get the customer ID
- const customerId = "customer_id";
-
+ // Authenticate the request and get the customer ID
+ const customerId = "customer_id";
return {
customerId,
- // To store the customer name and email
customerData: { name: "", email: "" },
};
},
diff --git a/vite/src/views/onboarding2/integrate/integration-steps/snippets/rr7Handler.tsx b/vite/src/views/onboarding2/integrate/integration-steps/snippets/rr7Handler.tsx
index 9cc41764c..6c4a3e15c 100644
--- a/vite/src/views/onboarding2/integrate/integration-steps/snippets/rr7Handler.tsx
+++ b/vite/src/views/onboarding2/integrate/integration-steps/snippets/rr7Handler.tsx
@@ -71,3 +71,23 @@ const handler = autumnHandler({
export const loader = handler.loader;
export const action = handler.action;`;
};
+export const rr7Other = (customerType: "user" | "org") => {
+ return `// app/routes/api.autumn.tsx
+
+import { autumnHandler } from "autumn-js/react-router";
+
+const handler = autumnHandler({
+ secretKey: process.env.AUTUMN_SECRET_KEY!,
+ identify: async (args) => {
+ const customerId = "your_customer_id"; // Get customer id from your database
+
+ return {
+ customerId,
+ customerData: { name: "", email: "" },
+ };
+ },
+});
+
+export const loader = handler.loader;
+export const action = handler.action;`;
+};
diff --git a/vite/src/views/onboarding2/integrate/select-stack/SelectFrameworks.tsx b/vite/src/views/onboarding2/integrate/select-stack/SelectFrameworks.tsx
new file mode 100644
index 000000000..5fe996607
--- /dev/null
+++ b/vite/src/views/onboarding2/integrate/select-stack/SelectFrameworks.tsx
@@ -0,0 +1,172 @@
+import { Code, Fingerprint, CircleUserRound, Building } from "lucide-react";
+import { useIntegrateContext } from "../IntegrateContext";
+import { Backend, Frontend } from "../StackEnums";
+
+export const SelectFrameworks = () => {
+ const { queryStates, setQueryStates } = useIntegrateContext();
+
+ const iconSize = 12;
+ const frontendOptions = [
+ { value: Frontend.Nextjs, label: "Next.js", logo: "nextjs.png" },
+ { value: Frontend.ReactRouter, label: "RR7", logo: "react-router.svg" },
+ { value: Frontend.Vite, label: "Vite SPA", logo: "vite.svg" },
+ {
+ value: Frontend.Other,
+ label: "Other",
+ icon: ,
+ },
+ ];
+
+ const backendOptions = [
+ { value: Backend.Nextjs, label: "Next.js", logo: "nextjs.png" },
+ {
+ value: Backend.ReactRouter,
+ label: "RR7",
+ logo: "react-router.svg",
+ },
+ { value: Backend.Hono, label: "Hono", logo: "hono.png" },
+ { value: Backend.Express, label: "Express", logo: "express.png" },
+ { value: Backend.Elysia, label: "Elysia", logo: "elysia.png" },
+ {
+ value: Backend.Other,
+ label: "Other",
+ icon: ,
+ },
+ ];
+
+ const authOptions = [
+ { value: "better_auth", label: "Better Auth", logo: "better-auth.png" },
+ { value: "supabase", label: "Supabase", logo: "supabase.png" },
+ { value: "clerk", label: "Clerk", logo: "clerk.png" },
+ {
+ value: "other",
+ label: "Other",
+ icon: ,
+ },
+ ];
+
+ const customerOptions = [
+ {
+ value: "user",
+ label: "Users",
+ icon: ,
+ },
+ {
+ value: "org",
+ label: "Orgs",
+ icon: ,
+ },
+ ];
+ return (
+
+
+
Frontend
+
+ {frontendOptions.map((option) => {
+ return (
+
+ );
+ })}
+
+
+
+
Backend
+
+ {backendOptions.map((option) => {
+ return (
+
+ );
+ })}
+
+
+
+
+
+
Auth Provider
+
+ {authOptions.map((option) => {
+ return (
+
+ );
+ })}
+
+
+
+
Customer Type
+
+ {customerOptions.map((option) => {
+ return (
+
+ );
+ })}
+
+
+
+
+ );
+};
+
+const FrameworkContainer = ({
+ option,
+ setQueryStates,
+ queryStates,
+ type,
+}: {
+ option: {
+ value: string;
+ label: string;
+ logo?: string;
+ icon?: React.ReactNode;
+ };
+ setQueryStates: (queryStates: any) => void;
+ queryStates: any;
+ type: "backend" | "auth" | "customerType" | "frontend";
+}) => {
+ const isSelected = queryStates[type] === option.value;
+ return (
+ setQueryStates({ ...queryStates, [type]: option.value })}
+ >
+ {option.logo ? (
+
+ ) : option.icon ? (
+ <>{option.icon}>
+ ) : (
+
+ )}
+
+ {option.label}
+
+
+ );
+};