Made onboarding dark mode and fixed to new plan editor.
This commit is contained in:
@@ -1,11 +1,10 @@
|
||||
// 1. productV2ToPlanType
|
||||
|
||||
import type { ProductItem } from "@models/productV2Models/productItemModels/productItemModels.js";
|
||||
import type {
|
||||
FrontendProduct,
|
||||
ProductV2,
|
||||
} from "@models/productV2Models/productV2Models.js";
|
||||
import { isFeatureItem, isFeaturePriceItem, notNullish } from "@utils/index.js";
|
||||
import { isFeaturePriceItem, notNullish } from "@utils/index.js";
|
||||
import { productV2ToBasePrice } from "@utils/productV3Utils/productItemUtils/productV3ItemUtils.js";
|
||||
|
||||
// 2. productV2ToBasePriceType
|
||||
@@ -40,7 +39,7 @@ export const productV2ToFrontendProduct = ({
|
||||
return {
|
||||
...product,
|
||||
planType:
|
||||
hasPricedFeatures || notNullish(basePrice?.amount) ? "paid" : "free",
|
||||
hasPricedFeatures || notNullish(basePrice?.price) ? "paid" : "free",
|
||||
basePriceType: basePrice?.interval
|
||||
? "recurring"
|
||||
: basePrice
|
||||
|
||||
@@ -32,7 +32,7 @@ const PlanFeatureRowPreview = ({ item }: { item: ProductItem }) => {
|
||||
const display = item.display || { primary_text: "", secondary_text: "" };
|
||||
|
||||
return (
|
||||
<div className="flex w-full h-9 items-center px-2 rounded-lg border border-border bg-card">
|
||||
<div className="flex w-full h-9 items-center px-2 rounded-lg border bg-card">
|
||||
{/* Left side - Icons and text */}
|
||||
<div className="flex flex-row items-center flex-1 gap-3 min-w-0">
|
||||
<div className="flex flex-row items-center gap-1 flex-shrink-0">
|
||||
@@ -73,7 +73,7 @@ export const PlanCardPreview = ({
|
||||
return (
|
||||
<Card
|
||||
className={cn(
|
||||
"min-w-[280px] max-w-md bg-card flex flex-col border-none shadow-[inset_0_0_0_0.5px_var(--t10)] !gap-0",
|
||||
"min-w-[280px] max-w-md bg-card flex flex-col border !gap-0",
|
||||
recommended && "ring-2 ring-primary",
|
||||
)}
|
||||
>
|
||||
@@ -81,7 +81,6 @@ export const PlanCardPreview = ({
|
||||
<div className="flex flex-col gap-2">
|
||||
{/* Title */}
|
||||
<h3 className="text-sub">{product.name}</h3>
|
||||
|
||||
{/* Price */}
|
||||
{product.items.filter((x) => isFeaturePriceItem(x as ProductItem))
|
||||
.length > 0 &&
|
||||
@@ -97,7 +96,6 @@ export const PlanCardPreview = ({
|
||||
) : (
|
||||
<span className="text-main">Free</span>
|
||||
)}
|
||||
|
||||
{/* Description */}
|
||||
{product.description && (
|
||||
<p className="text-body-secondary line-clamp-2">
|
||||
@@ -121,7 +119,6 @@ export const PlanCardPreview = ({
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Action button */}
|
||||
{/* <Tooltip> */}
|
||||
{/* <TooltipTrigger asChild> */}
|
||||
|
||||
@@ -18,7 +18,7 @@ export const CustomToaster = () => {
|
||||
toastOptions={{
|
||||
duration: 6000,
|
||||
className:
|
||||
"w-96 px-3 pt-3 pb-2.5 bg-white rounded-xl shadow-[0_4px_4px_0_rgba(0,0,0,0.02),inset_0_-4px_6px_0_rgba(0,0,0,0.04),0_4px_24px_0_rgba(0,0,0,0.03)] outline outline-16 inline-flex flex-row justify-start items-start gap-3 overflow-hidden",
|
||||
"w-96 px-3 pt-3 pb-2.5 rounded-xl shadow-[0_4px_4px_0_rgba(0,0,0,0.02),inset_0_-4px_6px_0_rgba(0,0,0,0.04),0_4px_24px_0_rgba(0,0,0,0.03)] outline outline-16 inline-flex flex-row justify-start items-start gap-3 overflow-hidden !bg-card !text-t1",
|
||||
style: {
|
||||
"--normal-border": "var(--t10)",
|
||||
} as React.CSSProperties,
|
||||
|
||||
@@ -45,11 +45,11 @@ const CodeGroupTab = React.forwardRef<
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex items-center justify-center flex-1 min-w-0 h-6 px-2 py-1 text-[13px] font-semibold tracking-[-0.039px] leading-normal whitespace-nowrap",
|
||||
"bg-white border border-t10 border-r-0",
|
||||
"text-t12 transition-none outline-none",
|
||||
"bg-interactive-secondary border border-r-0",
|
||||
"text-t3 transition-none outline-none",
|
||||
"hover:text-primary",
|
||||
"focus-visible:text-primary",
|
||||
"data-[state=active]:bg-neutral-50 data-[state=active]:shadow-[0px_3px_4px_0px_inset_rgba(0,0,0,0.04)]",
|
||||
"data-[state=active]:bg-interactive-secondary-hover data-[state=active]:shadow-[0px_3px_4px_0px_inset_rgba(0,0,0,0.04)]",
|
||||
"data-[state=inactive]:shadow-[0px_-3px_4px_0px_inset_rgba(0,0,0,0.04)]",
|
||||
"first:rounded-tl-md first:border-l",
|
||||
className,
|
||||
@@ -87,7 +87,7 @@ const CodeGroupCopyButton = React.forwardRef<
|
||||
onClick={handleClick}
|
||||
className={cn(
|
||||
"flex items-center justify-center h-6 px-2 py-1",
|
||||
"bg-white border border-t10 rounded-tr-md",
|
||||
"bg-interactive-secondary border rounded-tr-md",
|
||||
"shadow-[0px_-3px_4px_0px_inset_rgba(0,0,0,0.04)]",
|
||||
"hover:text-primary transition-none outline-none",
|
||||
"focus-visible:text-primary",
|
||||
@@ -180,7 +180,7 @@ const CodeGroupContent = React.forwardRef<
|
||||
<TabsPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"bg-white border border-t10 border-t-0 rounded-bl-lg rounded-br-lg",
|
||||
"bg-white dark:bg-background border border-t-0 rounded-bl-lg rounded-br-lg",
|
||||
"p-4 outline-none",
|
||||
"cursor-pointer transition-colors",
|
||||
"hover:bg-neutral-50/50",
|
||||
|
||||
@@ -38,8 +38,8 @@ export default function StackBadge({
|
||||
className={cn(
|
||||
"pr-2 rounded-md shadow-[0px_4px_4px_0px_rgba(0,0,0,0.02)] shadow-[inset_0px_-3px_4px_0px_rgba(0,0,0,0.04)] outline outline-1 outline-offset-[-1px] inline-flex justify-start items-center gap-1.5 transition-none",
|
||||
isSelected
|
||||
? "bg-white outline-primary"
|
||||
: "bg-white outline-neutral-300",
|
||||
? "bg-interactive-secondary outline-primary"
|
||||
: "bg-interactive-secondary outline-border",
|
||||
isClickable && !isSelected && "hover:outline-primary cursor-pointer",
|
||||
isClickable && "focus-visible:outline-primary focus-visible:outline-2",
|
||||
className,
|
||||
@@ -49,8 +49,8 @@ export default function StackBadge({
|
||||
className={cn(
|
||||
"size-auto p-1 rounded-tl rounded-bl shadow-[inset_0px_-3px_4px_0px_rgba(0,0,0,0.06)] shadow-[inset_0px_3px_4px_0px_rgba(255,255,255,0.10)] outline outline-1 outline-offset-[-1px] flex justify-center items-center gap-2.5",
|
||||
isSelected
|
||||
? "bg-light-purple outline-primary"
|
||||
: "bg-neutral-50 outline-neutral-300",
|
||||
? "bg-interactive-secondary outline-primary"
|
||||
: "bg-interactive-secondary outline-border",
|
||||
)}
|
||||
>
|
||||
{asset ? (
|
||||
@@ -62,7 +62,7 @@ export default function StackBadge({
|
||||
<div
|
||||
className={cn(
|
||||
"justify-start text-xs font-medium font-['Inter']",
|
||||
isSelected ? "text-primary" : "text-neutral-700",
|
||||
isSelected ? "text-primary" : "text-t2",
|
||||
)}
|
||||
>
|
||||
{stack}
|
||||
|
||||
@@ -33,7 +33,7 @@ export const GroupedTabButton = ({
|
||||
type="button"
|
||||
onClick={() => onValueChange(option.value)}
|
||||
className={cn(
|
||||
"w-full flex items-center justify-center gap-1 px-[6px] py-1 h-6 text-body border transition-none outline-none whitespace-nowrap",
|
||||
"w-full flex items-center justify-center gap-1 px-[6px] py-1 h-6 text-body border transition-none outline-none whitespace-nowrap !bg-interactive-secondary",
|
||||
"hover:text-primary focus-visible:text-primary",
|
||||
isActive &&
|
||||
" text-primary shadow-[0px_3px_4px_0px_inset_rgba(0,0,0,0.04)]",
|
||||
|
||||
@@ -44,7 +44,7 @@ export const ShortcutButton = ({
|
||||
const variantClasses =
|
||||
variant === "secondary"
|
||||
? "bg-muted text-body-secondary"
|
||||
: "bg-purple-medium dark:bg-interactive-secondary !text-primary-foreground";
|
||||
: "bg-purple-medium dark:bg-interactive-secondary";
|
||||
|
||||
return (
|
||||
<div className={`${baseClasses} ${variantClasses}`}>
|
||||
|
||||
@@ -23,7 +23,6 @@ export function SheetHeader({
|
||||
return (
|
||||
<div className={cn("p-4 pb-0", className)}>
|
||||
<h2 className="text-main">{title}</h2>
|
||||
|
||||
{/* check typography */}
|
||||
<p
|
||||
className={cn(
|
||||
|
||||
@@ -12,7 +12,7 @@ interface ThemeContextType {
|
||||
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
|
||||
|
||||
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
||||
const [theme, setTheme] = useLocalStorage<Theme>("theme", "light");
|
||||
const [theme, setTheme] = useLocalStorage<Theme>("theme", "system");
|
||||
const [isDark, setIsDark] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -122,8 +122,8 @@ html {
|
||||
|
||||
/* --input: oklch(0.92 0.004 286.32); */
|
||||
--ring: oklch(0.705 0.015 286.067);
|
||||
|
||||
--chart-1: #9c5aff;
|
||||
--chart-1: var(--primary);
|
||||
/* --chart-2: #9c5aff; */
|
||||
--chart-2: #a97eff;
|
||||
--chart-3: #8268ff;
|
||||
--chart-4: #7571ff;
|
||||
@@ -196,11 +196,11 @@ html {
|
||||
/* --border: oklch(0.274 0.006 286.033); */
|
||||
--input: oklch(0.274 0.006 286.033);
|
||||
--ring: oklch(0.442 0.017 285.786);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
/* --chart-1: oklch(0.488 0.243 264.376); */
|
||||
/* --chart-2: oklch(0.696 0.17 162.48); */
|
||||
/* --chart-3: oklch(0.769 0.188 70.08); */
|
||||
/* --chart-4: oklch(0.627 0.265 303.9); */
|
||||
/* --chart-5: oklch(0.645 0.246 16.439); */
|
||||
--sidebar: oklch(0.21 0.006 285.885);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
0px 3px 15px -5px #6100f2 inset,
|
||||
0px -2px 6px 0px #b27fff59 inset,
|
||||
0px 3px 10px 2px #00000040,
|
||||
0px 2px 0px 1px #661ecfb2 inset !important;
|
||||
0px 2px 0px 1px #661ecfb2 inset !important
|
||||
} */
|
||||
|
||||
.dark .btn-primary-dark {
|
||||
|
||||
@@ -6,11 +6,10 @@ import { useNavigate } from "react-router";
|
||||
import { toast } from "sonner";
|
||||
import { z } from "zod";
|
||||
import { CustomToaster } from "@/components/general/CustomToaster";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { IconButton } from "@/components/v2/buttons/IconButton";
|
||||
import { Input } from "@/components/v2/inputs/Input";
|
||||
import { useOrg } from "@/hooks/common/useOrg";
|
||||
import { authClient, signIn } from "@/lib/auth-client";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { getBackendErr } from "@/utils/genUtils";
|
||||
import { OTPSignIn } from "./components/OTPSignIn";
|
||||
|
||||
@@ -117,17 +116,16 @@ export const SignIn = () => {
|
||||
{!otpSent && (
|
||||
<div className="space-y-6">
|
||||
{/* Google Sign In Button */}
|
||||
<Button
|
||||
variant="auth"
|
||||
<IconButton
|
||||
variant="primary"
|
||||
onClick={handleGoogleSignIn}
|
||||
isLoading={googleLoading}
|
||||
startIcon={
|
||||
<FontAwesomeIcon icon={faGoogle} className="text-stone-400" />
|
||||
}
|
||||
className={height}
|
||||
icon={<FontAwesomeIcon icon={faGoogle} />}
|
||||
className={"w-full gap-2"}
|
||||
>
|
||||
{" "}
|
||||
Continue with Google
|
||||
</Button>
|
||||
</IconButton>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="relative">
|
||||
@@ -141,30 +139,28 @@ export const SignIn = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
className="text-base"
|
||||
autoComplete="email"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<Input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
className="text-base !w-full"
|
||||
autoComplete="email"
|
||||
/>
|
||||
|
||||
{/* Sign In Button */}
|
||||
<Button
|
||||
<IconButton
|
||||
type="submit"
|
||||
variant="auth"
|
||||
variant="secondary"
|
||||
isLoading={sendOtpLoading}
|
||||
onClick={handleEmailSignIn}
|
||||
className={cn(height)}
|
||||
startIcon={<Mail size={14} className="text-stone-500" />}
|
||||
className={"gap-2 w-full"}
|
||||
icon={<Mail size={14} className="text-t4" />}
|
||||
>
|
||||
Continue with email
|
||||
</Button>
|
||||
Continue with Email
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -86,12 +86,33 @@ export default function CustomerProductView() {
|
||||
}
|
||||
}, [entityIdParam]);
|
||||
|
||||
useEffect(() => {
|
||||
const root = document.documentElement;
|
||||
const previousTheme = root.classList.contains("dark") ? "dark" : "light";
|
||||
|
||||
// Force light mode
|
||||
root.classList.remove("dark");
|
||||
root.classList.add("light");
|
||||
|
||||
// Cleanup: restore previous theme when component unmounts
|
||||
return () => {
|
||||
root.classList.remove("light");
|
||||
if (previousTheme === "dark") {
|
||||
root.classList.add("dark");
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!originalProduct) return;
|
||||
|
||||
const product = originalProduct;
|
||||
|
||||
console.log('[CPV] effect', { prodId: originalProduct.id, v: originalProduct.version, cusId: cusProduct?.id });
|
||||
console.log("[CPV] effect", {
|
||||
prodId: originalProduct.id,
|
||||
v: originalProduct.version,
|
||||
cusId: cusProduct?.id,
|
||||
});
|
||||
|
||||
// Update initialProductRef BEFORE setProduct to ensure useAttachState
|
||||
// effect has the correct baseline when it runs
|
||||
|
||||
@@ -10,7 +10,7 @@ export const CodeSpan = ({
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
"bg-stone-200 font-mono text-t2 px-1 py-0.5 rounded-md",
|
||||
"bg-interactive-secondary font-mono text-t2 px-1 py-0.5 rounded-md",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { productV2ToBasePrice } from "@autumn/shared";
|
||||
import { PricingTableContainer } from "@/components/autumn/PricingTableContainer";
|
||||
import { PlanTypeBadges } from "@/components/v2/badges/PlanTypeBadges";
|
||||
import { Card, CardContent, CardHeader } from "@/components/v2/cards/Card";
|
||||
@@ -78,7 +77,7 @@ export const OnboardingPreview = ({
|
||||
return (
|
||||
<Card
|
||||
className={cn(
|
||||
"min-w-[28rem] max-w-xl mx-4 bg-card border-border border-[0.5px] p-4",
|
||||
"min-w-[28rem] max-w-xl mx-4 bg-card shadow-none p-4",
|
||||
showDummyFeature && !showFeatures && "pb-0",
|
||||
currentStep === 1 && "!gap-0",
|
||||
)}
|
||||
|
||||
@@ -100,16 +100,16 @@ export default function OnboardingContent() {
|
||||
{/* Standard layout for all steps - NO PROP DRILLING! */}
|
||||
<div
|
||||
className={cn(
|
||||
"relative w-full h-full flex bg-gray-medium [scrollbar-gutter:stable]",
|
||||
"relative w-full h-full flex bg-background [scrollbar-gutter:stable]",
|
||||
step === OnboardingStep.Integration
|
||||
? "overflow-y-auto bg-t13"
|
||||
? "overflow-y-auto"
|
||||
: "overflow-y-hidden",
|
||||
)}
|
||||
>
|
||||
{/* Exit button */}
|
||||
<ExitButton />
|
||||
|
||||
<div className="w-4/5 flex items-center justify-center relative ">
|
||||
<div className="w-4/5 flex items-center justify-center relative">
|
||||
{/* Components access Zustand directly - no props! */}
|
||||
{step === OnboardingStep.Integration ? (
|
||||
<div className="w-full h-full flex items-start justify-center py-8 pl-10">
|
||||
@@ -139,15 +139,15 @@ export default function OnboardingContent() {
|
||||
)}
|
||||
>
|
||||
<div className="rounded-lg h-full flex flex-col p-1 gap-[0.625rem] overflow-x-hidden">
|
||||
<div className="bg-card border-base border overflow-x-hidden rounded-[12px] mt-1 p-4 shrink-0">
|
||||
<div className=" overflow-x-hidden rounded-[12px] mt-1 p-4 shrink-0">
|
||||
<StepHeader />
|
||||
</div>
|
||||
{step !== OnboardingStep.Integration && (
|
||||
<SheetContainer className="bg-card border-base border overflow-x-hidden rounded-[12px] p-0 flex-1 pr-0">
|
||||
<SheetContainer className="bg-card border overflow-x-hidden rounded-[12px] p-0 flex-1 pr-0">
|
||||
<OnboardingStepRenderer />
|
||||
</SheetContainer>
|
||||
)}
|
||||
<div className="bg-card border-base border rounded-[12px] flex flex-col p-4 shrink-0">
|
||||
<div className=" rounded-[12px] flex flex-col p-4 shrink-0">
|
||||
<div className="flex items-center justify-center">
|
||||
<OnboardingSteps />
|
||||
</div>
|
||||
|
||||
@@ -5,8 +5,8 @@ import { useFeatureStore } from "@/hooks/stores/useFeatureStore";
|
||||
import { useProductStore } from "@/hooks/stores/useProductStore";
|
||||
import { useSheetStore } from "@/hooks/stores/useSheetStore";
|
||||
import { getItemId } from "@/utils/product/productItemUtils";
|
||||
import { EditPlanSheet } from "@/views/products/plan/components/EditPlanSheet";
|
||||
import { ProductItemContext } from "@/views/products/product/product-item/ProductItemContext";
|
||||
import { EditPlanSheet } from "../../products/plan/components/EditPlanSheet";
|
||||
import { EditPlanFeatureSheet } from "../../products/plan/components/edit-plan-feature/EditPlanFeatureSheet";
|
||||
import { NewFeatureSheet } from "../../products/plan/components/new-feature/NewFeatureSheet";
|
||||
import { SelectFeatureSheet } from "../../products/plan/components/SelectFeatureSheet";
|
||||
|
||||
@@ -38,7 +38,7 @@ export const OnboardingSteps = ({ className }: OnboardingStepsProps) => {
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="self-stretch w-full h-6 px-2.5 py-3 bg-white rounded-lg shadow-[inset_0px_-3px_4px_0px_rgba(0,0,0,0.04)] outline-1 outline-offset-[-1px] outline-border inline-flex justify-start items-center gap-1">
|
||||
<div className="self-stretch w-full h-6 px-2.5 py-3 bg-interactive-secondary rounded-lg shadow-[inset_0px_-3px_4px_0px_rgba(0,0,0,0.04)] outline-1 outline-offset-[-1px] outline-border inline-flex justify-start items-center gap-1">
|
||||
{Array.from({ length: totalSteps }).map((_, index) => {
|
||||
const isCompleted = index < currentStep;
|
||||
return (
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Input } from "@/components/v2/inputs/Input";
|
||||
import { SheetSection } from "@/components/v2/sheets/InlineSheet";
|
||||
import { useAutoSlug } from "@/hooks/common/useAutoSlug";
|
||||
import { useProductStore } from "@/hooks/stores/useProductStore";
|
||||
import { PlanTypeSection } from "@/views/products/plan/components/edit-plan-details/PlanTypeSection";
|
||||
import { BasePriceSection } from "../../products/plan/components/edit-plan-details/BasePriceSection";
|
||||
|
||||
export const PlanDetailsStep = () => {
|
||||
@@ -35,7 +36,7 @@ export const PlanDetailsStep = () => {
|
||||
className="mb-1"
|
||||
/>
|
||||
|
||||
<span className="text-form-label block">
|
||||
<span className="text-t4 text-sm block">
|
||||
This name will be displayed on Stripe's checkout page
|
||||
</span>
|
||||
</div>
|
||||
@@ -47,7 +48,7 @@ export const PlanDetailsStep = () => {
|
||||
onChange={(e) => setTarget(e.target.value)}
|
||||
className="mb-1"
|
||||
/>
|
||||
<span className="text-form-label block">
|
||||
<span className="text-t4 text-sm block">
|
||||
Used to refer to this plan when using Autumn's APIs or SDKs
|
||||
</span>
|
||||
</div>
|
||||
@@ -69,7 +70,7 @@ export const PlanDetailsStep = () => {
|
||||
</div>
|
||||
</div>
|
||||
</SheetSection>
|
||||
|
||||
<PlanTypeSection />
|
||||
<BasePriceSection withSeparator={false} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -31,7 +31,8 @@ export const StepHeader = () => {
|
||||
|
||||
return (
|
||||
<SheetHeader
|
||||
title={`Step ${stepNum}: ${config.title}`}
|
||||
// title={`Step ${stepNum}: ${config.title}`}
|
||||
title={`${config.title}`}
|
||||
description={config.description}
|
||||
noSeparator={true}
|
||||
className="p-0 sticky"
|
||||
|
||||
@@ -26,10 +26,10 @@ export const MCPSection = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="px-3 py-3.5 bg-gray-50 rounded-lg outline-[1.50px] outline-offset-[-1.50px] outline-violet-600 flex flex-col items-start gap-4">
|
||||
<div className="px-3 py-3.5 bg-interactive-secondary rounded-lg outline-[1.50px] outline-offset-[-1.50px] outline-violet-600 flex flex-col items-start gap-4">
|
||||
<div className="inline-flex justify-start items-center gap-1.5">
|
||||
<InfoIcon fill="var(--primary)" size={16} weight="fill" />
|
||||
<div className="justify-start text-zinc-800 text-xs font-semibold font-['Inter']">
|
||||
<div className="justify-start text-t2 text-xs font-semibold font-['Inter']">
|
||||
Install our MCP and prompt the model to use the `autumn-docs` MCP to
|
||||
integrate Autumn
|
||||
</div>
|
||||
|
||||
@@ -27,11 +27,11 @@ const NextStepCard = ({ title, description, href }: NextStepCardProps) => {
|
||||
<Card
|
||||
className={cn(
|
||||
"group gap-1 my-0 cursor-pointer",
|
||||
"hover:!bg-hover-primary hover:border-primary",
|
||||
"hover:!bg-interactive-secondary-hover hover:border-primary",
|
||||
)}
|
||||
>
|
||||
<CardHeader className="flex flex-row items-center justify-between">
|
||||
<CardTitle className="text-sub text-t9">{title}</CardTitle>
|
||||
<CardTitle className="text-sub text-t2">{title}</CardTitle>
|
||||
<IconButton
|
||||
icon={<ArrowSquareOutIcon className="ml-auto" />}
|
||||
variant="skeleton"
|
||||
@@ -53,7 +53,7 @@ export const NextStepsSection = () => {
|
||||
<SectionHeader
|
||||
stepNumber={7}
|
||||
title={<span>Next Steps</span>}
|
||||
description="Congrats on setting up Autumn! The next steps are to learn how to use Autumn to check if a user has access to features in your application, and track usage for those features. Learn how to do so here.√"
|
||||
description="Congrats on setting up Autumn! The next steps are to learn how to use Autumn to check if a user has access to features in your application, and track usage for those features. Learn how to do so here."
|
||||
/>
|
||||
|
||||
<div className="pl-[32px] flex flex-col gap-6">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { productV2ToFrontendProduct } from "@autumn/shared";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useProductsQuery } from "@/hooks/queries/useProductsQuery";
|
||||
import { useProductStore } from "@/hooks/stores/useProductStore";
|
||||
@@ -45,9 +46,14 @@ export const useOnboardingProductSync = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// Convert ProductV2 to FrontendProduct before setting in store
|
||||
const frontendProduct = productV2ToFrontendProduct({
|
||||
product: selectedProduct,
|
||||
});
|
||||
|
||||
// Set both product and baseProduct
|
||||
setBaseProduct(selectedProduct);
|
||||
setProduct(selectedProduct);
|
||||
setBaseProduct(frontendProduct);
|
||||
setProduct(frontendProduct);
|
||||
|
||||
if (!hasInitialized.current) {
|
||||
hasInitialized.current = true;
|
||||
|
||||
@@ -4,7 +4,9 @@ import {
|
||||
FeatureUsageType,
|
||||
isPriceItem,
|
||||
productV2ToBasePrice,
|
||||
productV2ToPlanType,
|
||||
} from "@autumn/shared";
|
||||
import { useHasChanges } from "@/hooks/stores/useProductStore";
|
||||
import { OnboardingStep } from "../utils/onboardingUtils";
|
||||
import { useOnboarding3QueryState } from "./useOnboarding3QueryState";
|
||||
|
||||
@@ -12,6 +14,7 @@ export const useOnboardingSteps = () => {
|
||||
// Use query state instead of local state
|
||||
const { queryStates } = useOnboarding3QueryState();
|
||||
const step = queryStates.step;
|
||||
const hasChanges = useHasChanges();
|
||||
|
||||
// Step validation
|
||||
const validateStep = (
|
||||
@@ -32,6 +35,9 @@ export const useOnboardingSteps = () => {
|
||||
// Base price validation - safely check if product has items
|
||||
if (!product.items) return false;
|
||||
|
||||
const planType = productV2ToPlanType({ product });
|
||||
if (!planType) return false;
|
||||
|
||||
const basePrice = productV2ToBasePrice({
|
||||
product: product as unknown as ProductV2,
|
||||
});
|
||||
@@ -59,7 +65,7 @@ export const useOnboardingSteps = () => {
|
||||
case OnboardingStep.FeatureConfiguration:
|
||||
return true;
|
||||
case OnboardingStep.Playground:
|
||||
return true;
|
||||
return !hasChanges;
|
||||
case OnboardingStep.Integration:
|
||||
return true;
|
||||
default:
|
||||
|
||||
@@ -61,22 +61,22 @@ export const getNextStep = (
|
||||
// Step configuration for headers and descriptions
|
||||
export const stepConfig = {
|
||||
[OnboardingStep.PlanDetails]: {
|
||||
title: "Create a plan",
|
||||
title: "Create your first Plan",
|
||||
description:
|
||||
"Plans are the pricing tiers your application offers. You can create your free tiers and your paid tiers too.",
|
||||
"Create the first plan your app offers. You can create both free and paid tiers.",
|
||||
},
|
||||
[OnboardingStep.FeatureCreation]: {
|
||||
title: "Create a feature",
|
||||
title: "Create a Feature",
|
||||
description:
|
||||
"Features are the benefits customers get access to when using this plan. You can create a feature for things in your app you want to limit, track or bill for.",
|
||||
"Features are what customers get access to with this plan. Create features for things you want to limit, track or bill for.",
|
||||
},
|
||||
[OnboardingStep.FeatureConfiguration]: {
|
||||
title: "Define feature limits or billing",
|
||||
title: "Configure Plan Limits and Pricing",
|
||||
description:
|
||||
"Features can be included as part of this plan, or billed for based on their usage.",
|
||||
"Set a limit for how much the feature can be used, or charge a usage-based price for it.",
|
||||
},
|
||||
[OnboardingStep.Playground]: {
|
||||
title: "Finish your setup",
|
||||
title: "Add more Features and Plans",
|
||||
description:
|
||||
"Take your time setting up plans and features. Revisit anytime. Use Preview Mode to test features, limits and upgrade/downgrade flows.",
|
||||
},
|
||||
|
||||
@@ -302,8 +302,8 @@ export const BasePriceSection = ({
|
||||
infoBox: "text-sm py-2",
|
||||
}}
|
||||
>
|
||||
You can add usage-based prices (eg, per seat, per credit) when you
|
||||
link a feature to this plan.
|
||||
You can charge based on the quantity of a feature (eg per seat,
|
||||
per credit) when you link features to this plan.
|
||||
</InfoBox>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user