diff --git a/vite/public/frameworks/Symbol Alternative.svg b/vite/public/frameworks/Symbol Alternative.svg new file mode 100644 index 000000000..cf7f77fb1 --- /dev/null +++ b/vite/public/frameworks/Symbol Alternative.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vite/public/frameworks/better-auth.png b/vite/public/frameworks/better-auth.png new file mode 100644 index 000000000..eefc8c85f Binary files /dev/null and b/vite/public/frameworks/better-auth.png differ diff --git a/vite/public/frameworks/clerk.png b/vite/public/frameworks/clerk.png new file mode 100644 index 000000000..919f9a97b Binary files /dev/null and b/vite/public/frameworks/clerk.png differ diff --git a/vite/public/frameworks/elysia.png b/vite/public/frameworks/elysia.png new file mode 100644 index 000000000..ad6210c51 Binary files /dev/null and b/vite/public/frameworks/elysia.png differ diff --git a/vite/public/frameworks/express.png b/vite/public/frameworks/express.png new file mode 100644 index 000000000..b748de7b4 Binary files /dev/null and b/vite/public/frameworks/express.png differ diff --git a/vite/public/frameworks/hono.png b/vite/public/frameworks/hono.png new file mode 100644 index 000000000..2c633ea06 Binary files /dev/null and b/vite/public/frameworks/hono.png differ diff --git a/vite/public/frameworks/nextjs.png b/vite/public/frameworks/nextjs.png new file mode 100644 index 000000000..a54c98329 Binary files /dev/null and b/vite/public/frameworks/nextjs.png differ diff --git a/vite/public/frameworks/react-router.svg b/vite/public/frameworks/react-router.svg new file mode 100644 index 000000000..a5c5dacca --- /dev/null +++ b/vite/public/frameworks/react-router.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/vite/public/frameworks/react.png b/vite/public/frameworks/react.png new file mode 100644 index 000000000..8bde5d08a Binary files /dev/null and b/vite/public/frameworks/react.png differ diff --git a/vite/public/frameworks/supabase.png b/vite/public/frameworks/supabase.png new file mode 100644 index 000000000..a5c9e1cff Binary files /dev/null and b/vite/public/frameworks/supabase.png differ diff --git a/vite/public/frameworks/vite.svg b/vite/public/frameworks/vite.svg new file mode 100644 index 000000000..de4aeddc1 --- /dev/null +++ b/vite/public/frameworks/vite.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/vite/src/components/ui/tabs.tsx b/vite/src/components/ui/tabs.tsx index 27d4b2d06..46b0d6de1 100644 --- a/vite/src/components/ui/tabs.tsx +++ b/vite/src/components/ui/tabs.tsx @@ -24,13 +24,17 @@ TabsList.displayName = TabsPrimitive.List.displayName; const TabsTrigger = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( + React.ComponentPropsWithoutRef & { + variant?: "default" | "onboarding"; + } +>(({ className, variant = "default", ...props }, ref) => ( diff --git a/vite/src/views/onboarding/onboarding-steps/CreateSecretKey.tsx b/vite/src/views/onboarding/onboarding-steps/CreateSecretKey.tsx index 6239ebd3a..203369856 100644 --- a/vite/src/views/onboarding/onboarding-steps/CreateSecretKey.tsx +++ b/vite/src/views/onboarding/onboarding-steps/CreateSecretKey.tsx @@ -21,13 +21,13 @@ export const CreateSecretKey = ({ setApiKey: (apiKey: string) => void; number: number; }) => { - let env = useEnv(); - let [apiKeyName, setApiKeyName] = useState(""); - let [apiCreated, setApiCreated] = useState(false); + const env = useEnv(); + const [apiKeyName, setApiKeyName] = useState(""); + const [apiCreated, setApiCreated] = useState(false); const [loading, setLoading] = useState(false); const [copied, setCopied] = useState(false); - let axiosInstance = useAxiosInstance({ env }); + const axiosInstance = useAxiosInstance({ env }); const handleCreate = async () => { console.log("creating api key", apiKeyName ? apiKeyName : name); diff --git a/vite/src/views/onboarding2/integrate/AITools.tsx b/vite/src/views/onboarding2/integrate/AITools.tsx index 6322f8cab..3b0ec56d2 100644 --- a/vite/src/views/onboarding2/integrate/AITools.tsx +++ b/vite/src/views/onboarding2/integrate/AITools.tsx @@ -1,8 +1,15 @@ import { StepHeader } from "./StepHeader"; import { Button } from "@/components/ui/button"; import CopyButton from "@/components/general/CopyButton"; -import { ExternalLink, Download } from "lucide-react"; +import { ExternalLink, Download, Info } from "lucide-react"; import { toast } from "sonner"; +import { + Accordion, + AccordionTrigger, + AccordionContent, + AccordionItem, +} from "@/components/ui/accordion"; +import FieldLabel from "@/components/general/modal-components/FieldLabel"; export const AITools = () => { // MCP configuration for Autumn @@ -33,7 +40,41 @@ export const AITools = () => { return (
- + {/* */} + + + +
+ + Add Autumn to your AI tools +
+
+ +
+ Cursor + +
+
+
+

If you're using Cursor or Claude Code, you can install our MCP server to use AI to integrate Autumn. @@ -41,16 +82,6 @@ export const AITools = () => { {/* One-click install for Cursor */}

- + + )}
); }; 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 ( +
+
+ +
+ {children} +
+ ); +}; 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, + }} + > +
+ {apiKey ? ( +
+ + +
+ ) : ( +
+ {/* setApiKeyName(e.target.value)} + /> */} + +
+ )} +
+
+ ); +}; + +{ + /*
+ {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 ( +
+
+ +
+ {frontendOptions.map((option) => { + return ( + + ); + })} +
+
+
+ +
+ {backendOptions.map((option) => { + return ( + + ); + })} +
+
+ +
+
+ +
+ {authOptions.map((option) => { + return ( + + ); + })} +
+
+
+ +
+ {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.label} + ) : option.icon ? ( + <>{option.icon} + ) : ( + + )} + + {option.label} + +
+ ); +};