fix: moved attach body to shared folder to fix zod error

This commit is contained in:
John Yeo
2025-07-15 19:24:25 +01:00
parent 731485903d
commit ea762e3fae
24 changed files with 58 additions and 516 deletions

View File

@@ -1,3 +0,0 @@
when importing db, or client, it should look like this:
import { db } from "@/db/initDrizzle.js";

View File

@@ -1,59 +0,0 @@
[*]
cpp_indent_braces=false
cpp_indent_multi_line_relative_to=innermost_parenthesis
cpp_indent_within_parentheses=indent
cpp_indent_preserve_within_parentheses=false
cpp_indent_case_labels=false
cpp_indent_case_contents=true
cpp_indent_case_contents_when_block=false
cpp_indent_lambda_braces_when_parameter=true
cpp_indent_goto_labels=one_left
cpp_indent_preprocessor=leftmost_column
cpp_indent_access_specifiers=false
cpp_indent_namespace_contents=true
cpp_indent_preserve_comments=false
cpp_new_line_before_open_brace_namespace=ignore
cpp_new_line_before_open_brace_type=ignore
cpp_new_line_before_open_brace_function=ignore
cpp_new_line_before_open_brace_block=ignore
cpp_new_line_before_open_brace_lambda=ignore
cpp_new_line_scope_braces_on_separate_lines=false
cpp_new_line_close_brace_same_line_empty_type=false
cpp_new_line_close_brace_same_line_empty_function=false
cpp_new_line_before_catch=true
cpp_new_line_before_else=true
cpp_new_line_before_while_in_do_while=false
cpp_space_before_function_open_parenthesis=remove
cpp_space_within_parameter_list_parentheses=false
cpp_space_between_empty_parameter_list_parentheses=false
cpp_space_after_keywords_in_control_flow_statements=true
cpp_space_within_control_flow_statement_parentheses=false
cpp_space_before_lambda_open_parenthesis=false
cpp_space_within_cast_parentheses=false
cpp_space_after_cast_close_parenthesis=false
cpp_space_within_expression_parentheses=false
cpp_space_before_block_open_brace=true
cpp_space_between_empty_braces=false
cpp_space_before_initializer_list_open_brace=false
cpp_space_within_initializer_list_braces=true
cpp_space_preserve_in_initializer_list=true
cpp_space_before_open_square_bracket=false
cpp_space_within_square_brackets=false
cpp_space_before_empty_square_brackets=false
cpp_space_between_empty_square_brackets=false
cpp_space_group_square_brackets=true
cpp_space_within_lambda_brackets=false
cpp_space_between_empty_lambda_brackets=false
cpp_space_before_comma=false
cpp_space_after_comma=true
cpp_space_remove_around_member_operators=true
cpp_space_before_inheritance_colon=true
cpp_space_before_constructor_colon=true
cpp_space_remove_before_semicolon=true
cpp_space_after_semicolon=false
cpp_space_remove_around_unary_operator=true
cpp_space_around_binary_operator=insert
cpp_space_around_assignment_operator=insert
cpp_space_pointer_reference_alignment=left
cpp_space_around_ternary_operator=insert
cpp_wrap_preserve_blocks=one_liners

24
pnpm-lock.yaml generated
View File

@@ -414,13 +414,8 @@ importers:
specifier: ^34.0.1
version: 34.0.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
autumn-js:
<<<<<<< HEAD
specifier: ^0.0.77
version: 0.0.77(react@18.2.0)
=======
specifier: ^0.0.93
version: 0.0.93(better-auth@1.2.9)(react@18.3.1)
>>>>>>> main
version: 0.0.93(better-auth@1.2.9)(better-call@1.0.12)(react@18.2.0)
axios:
specifier: ^1.8.3
version: 1.8.3
@@ -11718,14 +11713,6 @@ snapshots:
attr-accept@2.2.5: {}
autumn-js@0.0.77(react@18.2.0):
dependencies:
chalk: 5.4.1
rou3: 0.6.3
swr: 2.3.4(react@18.2.0)
optionalDependencies:
react: 18.2.0
autumn-js@0.0.77(react@18.3.1):
dependencies:
chalk: 5.4.1
@@ -11734,15 +11721,16 @@ snapshots:
optionalDependencies:
react: 18.3.1
autumn-js@0.0.93(better-auth@1.2.9)(react@18.3.1):
autumn-js@0.0.93(better-auth@1.2.9)(better-call@1.0.12)(react@18.2.0):
dependencies:
chalk: 5.4.1
rou3: 0.6.3
swr: 2.3.3(react@18.3.1)
zod: 3.25.64
swr: 2.3.4(react@18.2.0)
zod: 3.25.76
optionalDependencies:
better-auth: 1.2.9
react: 18.3.1
better-call: 1.0.12
react: 18.2.0
axios@1.10.0:
dependencies:

View File

@@ -18,7 +18,7 @@ import {
TrackParams,
UsageParams,
} from "autumn-js";
import { AttachBody } from "@/internal/customers/attach/models/AttachBody.js";
import { AttachBody } from "@autumn/shared";
export default class AutumnError extends Error {
message: string;
@@ -104,14 +104,14 @@ export class AutumnInt {
deleteInStripe = false,
}: {
deleteInStripe?: boolean;
} = {},
} = {}
) {
const response = await fetch(
`${this.baseUrl}${path}?${deleteInStripe ? "delete_in_stripe=true" : ""}`,
{
method: "DELETE",
headers: this.headers,
},
}
);
if (response.status != 200) {
@@ -216,7 +216,7 @@ export class AutumnInt {
customerId: string,
params?: {
expand?: CusExpand[];
},
}
): Promise<
Customer & {
invoices: any[];
@@ -233,7 +233,7 @@ export class AutumnInt {
}
const data = await this.get(
`/customers/${customerId}?${queryParams.toString()}`,
`/customers/${customerId}?${queryParams.toString()}`
);
return data;
},
@@ -248,7 +248,7 @@ export class AutumnInt {
deleteInStripe = false,
}: {
deleteInStripe?: boolean;
} = {},
} = {}
) => {
const data = await this.delete(`/customers/${customerId}`, {
deleteInStripe,
@@ -260,19 +260,19 @@ export class AutumnInt {
entities = {
get: async (customerId: string, entityId: string) => {
const data = await this.get(
`/customers/${customerId}/entities/${entityId}?expand=${EntityExpand.Invoices}`,
`/customers/${customerId}/entities/${entityId}?expand=${EntityExpand.Invoices}`
);
return data;
},
create: async (
customerId: string,
entity: CreateEntity | CreateEntity[],
entity: CreateEntity | CreateEntity[]
) => {
// let entities = Array.isArray(entity) ? entity : [entity];
const data = await this.post(
`/customers/${customerId}/entities?with_autumn_id=true`,
entity,
entity
);
return data;
@@ -285,7 +285,7 @@ export class AutumnInt {
delete: async (customerId: string, entityId: string) => {
const data = await this.delete(
`/customers/${customerId}/entities/${entityId}`,
`/customers/${customerId}/entities/${entityId}`
);
return data;
},
@@ -302,10 +302,10 @@ export class AutumnInt {
get: async (
productId: string,
{ v1Schema = false }: { v1Schema?: boolean } = {},
{ v1Schema = false }: { v1Schema?: boolean } = {}
) => {
const data = await this.get(
`/products/${productId}?${v1Schema ? "schemaVersion=1" : ""}`,
`/products/${productId}?${v1Schema ? "schemaVersion=1" : ""}`
);
return data;
},

View File

@@ -1,7 +1,7 @@
import { checkToAttachParams } from "@/internal/customers/attach/attachUtils/attachParams/checkToAttachParams.js";
import { FullCustomer, FullProduct } from "@autumn/shared";
import { ExtendedRequest } from "@/utils/models/Request.js";
import { AttachBody } from "@/internal/customers/attach/models/AttachBody.js";
import { AttachBody } from "@autumn/shared";
import { attachParamsToPreview } from "@/internal/customers/attach/handleAttachPreview/attachParamsToPreview.js";
import {

View File

@@ -29,7 +29,7 @@ import { orgToVersion } from "@/utils/versionUtils.js";
import { handleAttachRaceCondition } from "@/external/redis/redisUtils.js";
import { routeHandler } from "@/utils/routerUtils.js";
import { ExtendedRequest, ExtendedResponse } from "@/utils/models/Request.js";
import { AttachBodySchema } from "./models/AttachBody.js";
import { AttachBodySchema } from "@autumn/shared";
import { processAttachBody } from "./attachUtils/attachParams/processAttachBody.js";
import { handleAttachPreview } from "./handleAttachPreview/handleAttachPreview.js";
import { handleAttach } from "./handleAttach.js";
@@ -113,7 +113,7 @@ export const handlePublicAttachErrors = async ({
// 1. If on paid plan, not allowed to switch product
const curProductFree = isFreeProduct(
curCusProduct?.customer_prices.map((cp: any) => cp.price) || [], // if no current product...
curCusProduct?.customer_prices.map((cp: any) => cp.price) || [] // if no current product...
);
if (!curProductFree) {
@@ -171,7 +171,7 @@ export const checkStripeConnections = async ({
env,
product,
logger,
}),
})
);
}
await Promise.all(batchProductUpdates);
@@ -205,7 +205,7 @@ export const createStripePrices = async ({
logger,
internalEntityId: attachParams.internalEntityId,
useCheckout,
}),
})
);
}
await Promise.all(batchPriceUpdates);
@@ -271,7 +271,7 @@ const handleAttachOld = async (req: any, res: any) =>
? customer.entities.find(
(e) =>
e.id === attachBody.entity_id ||
e.internal_id === attachBody.entity_id,
e.internal_id === attachBody.entity_id
)?.internal_id
: undefined;

View File

@@ -5,7 +5,7 @@ import RecaseError from "@/utils/errorUtils.js";
import { notNullish } from "@/utils/genUtils.js";
import { ExtendedRequest } from "@/utils/models/Request.js";
import { ErrCode, CusProductStatus } from "@autumn/shared";
import { AttachBody } from "../../../models/AttachBody.js";
import { AttachBody } from "@autumn/shared";
const getProductsForAttach = async ({
req,
@@ -37,7 +37,7 @@ const getProductsForAttach = async ({
for (const prod of products) {
let otherProd = products.find(
(p) => p.group === prod.group && !p.is_add_on && p.id !== prod.id,
(p) => p.group === prod.group && !p.is_add_on && p.id !== prod.id
);
if (otherProd && !otherProd.is_add_on && !isOneOff(prod.prices)) {

View File

@@ -19,7 +19,7 @@ import {
Entitlement,
CreateFreeTrial,
} from "@autumn/shared";
import { AttachBody } from "../../../models/AttachBody.js";
import { AttachBody } from "@autumn/shared";
import { mapOptionsList } from "../../mapOptionsList.js";
export const getPricesAndEnts = async ({

View File

@@ -1,5 +1,5 @@
import { ExtendedRequest } from "@/utils/models/Request.js";
import { AttachBody } from "../../models/AttachBody.js";
import { AttachBody } from "@autumn/shared";
import { processAttachBody } from "./processAttachBody.js";
import { orgToVersion } from "@/utils/versionUtils.js";
import { APIVersion } from "@autumn/shared";

View File

@@ -1,5 +1,5 @@
import { ExtendedRequest } from "@/utils/models/Request.js";
import { AttachBody } from "../../models/AttachBody.js";
import { AttachBody } from "@autumn/shared";
import { createStripeCli } from "@/external/stripe/utils.js";
import { getStripeCusData } from "./attachParamsUtils/getStripeCusData.js";
import { getPricesAndEnts } from "./attachParamsUtils/getPricesAndEnts.js";

View File

@@ -1,6 +1,6 @@
import { ExtendedRequest } from "@/utils/models/Request.js";
import { AttachBody } from "../models/AttachBody.js";
import { AttachBody } from "@autumn/shared";
import { AttachParams } from "../../cusProducts/AttachParams.js";
import { notNullish } from "@/utils/genUtils.js";
import { AttachBranch, AttachErrCode, BillingInterval } from "@autumn/shared";
@@ -91,7 +91,7 @@ const getOptionsToUpdate = ({
for (const newOptions of newOptionsList) {
let internalFeatureId = newOptions.internal_feature_id;
let existingOptions = oldOptionsList.find(
(o) => o.internal_feature_id === internalFeatureId,
(o) => o.internal_feature_id === internalFeatureId
);
let price = findPrepaidPrice({

View File

@@ -1,7 +1,7 @@
import { AttachParams } from "../../cusProducts/AttachParams.js";
import { AttachFlags } from "../models/AttachFlags.js";
import { AttachConfig, AttachBranch } from "@autumn/shared";
import { AttachBody } from "../models/AttachBody.js";
import { AttachBody } from "@autumn/shared";
import { isFreeProduct } from "@/internal/products/productUtils.js";
import { nullish } from "@/utils/genUtils.js";
import { ProrationBehavior } from "@autumn/shared";

View File

@@ -12,7 +12,7 @@ import {
import { handleUpgradeDiffInterval } from "../attachFunctions/upgradeDiffIntFlow/handleUpgradeDiffInt.js";
import { handleCreateCheckout } from "../../add-product/handleCreateCheckout.js";
import { handleAddProduct } from "../attachFunctions/addProductFlow/handleAddProduct.js";
import { AttachBody } from "../models/AttachBody.js";
import { AttachBody } from "@autumn/shared";
import { AttachConfig } from "@autumn/shared";
import { handleScheduleFunction } from "../attachFunctions/scheduleFlow/handleScheduleFunction.js";
import { handleUpdateQuantityFunction } from "../attachFunctions/updateQuantityFlow/updateQuantityFlow.js";
@@ -136,7 +136,7 @@ export const runAttachFunction = async ({
logger.info(`--------------------------------`);
logger.info(
`ATTACHING ${productIdsStr} to ${customer.name} (${customer.id || customer.email}), org: ${org.slug}\n`,
`ATTACHING ${productIdsStr} to ${customer.name} (${customer.id || customer.email}), org: ${org.slug}\n`
);
if (customer.entity) {
logger.info(`Entity: ${customer.entity.name} (${customer.entity.id})`);
@@ -149,7 +149,7 @@ export const runAttachFunction = async ({
curSameProduct: curSameProduct?.product.id,
curScheduledProduct: curScheduledProduct?.product.id,
},
},
}
);
// config.proration = ProrationBehavior.None;
@@ -206,7 +206,7 @@ export const runAttachFunction = async ({
product_ids: attachParams.products.map((p) => p.id),
code: SuccessCode.RenewedProduct,
message: `Successfully renewed product ${attachParams.products[0].id}`,
}),
})
);
return;
}

View File

@@ -3,7 +3,7 @@ import { ErrCode } from "@/errors/errCodes.js";
import { StatusCodes } from "http-status-codes";
import { AttachParams } from "../../cusProducts/AttachParams.js";
import { AttachBranch, AttachErrCode, UsagePriceConfig } from "@autumn/shared";
import { AttachBody } from "../models/AttachBody.js";
import { AttachBody } from "@autumn/shared";
import { AttachConfig, AttachFlags } from "../models/AttachFlags.js";
import {
getEntOptions,
@@ -158,7 +158,7 @@ const handleUpdateQuantityErrors = async ({
const allowance = getResetBalance({
entitlement: curr.entitlement,
options: cusProduct.options.find(
(o) => o.internal_feature_id == option.internal_feature_id,
(o) => o.internal_feature_id == option.internal_feature_id
),
relatedPrice: price,
});

View File

@@ -1,7 +1,7 @@
import { handleAttachRaceCondition } from "@/external/redis/redisUtils.js";
import { ExtendedRequest, ExtendedResponse } from "@/utils/models/Request.js";
import { routeHandler } from "@/utils/routerUtils.js";
import { AttachBodySchema } from "./models/AttachBody.js";
import { AttachBodySchema } from "@autumn/shared";
import { getAttachParams } from "./attachUtils/attachParams/getAttachParams.js";
import { getAttachBranch } from "./attachUtils/getAttachBranch.js";
import { getAttachConfig } from "./attachUtils/getAttachConfig.js";

View File

@@ -1,6 +1,6 @@
import { ExtendedRequest } from "@/utils/models/Request.js";
import { AttachParams } from "../../cusProducts/AttachParams.js";
import { AttachBody } from "../models/AttachBody.js";
import { AttachBody } from "@autumn/shared";
import { getAttachBranch } from "../attachUtils/getAttachBranch.js";
import { getAttachConfig } from "../attachUtils/getAttachConfig.js";
import { AttachFunction } from "@autumn/shared";

View File

@@ -1,7 +1,7 @@
import { routeHandler } from "@/utils/routerUtils.js";
import { getAttachParams } from "../attachUtils/attachParams/getAttachParams.js";
import { AttachBodySchema } from "../models/AttachBody.js";
import { AttachBodySchema } from "@autumn/shared";
import { ExtendedResponse } from "@/utils/models/Request.js";
import { ExtendedRequest } from "@/utils/models/Request.js";

View File

@@ -59,25 +59,6 @@ export const updateCustomerDetails = async ({
return customer;
};
const CusProductResultSchema = CusProductSchema.extend({
customer: CustomerSchema,
product: ProductSchema,
});
export const flipProductResults = (
cusProducts: z.infer<typeof CusProductResultSchema>[],
) => {
const customers = [];
for (const cusProduct of cusProducts) {
customers.push({
...cusProduct.customer,
customer_products: [cusProduct],
});
}
return customers;
};
export const getCusInvoices = async ({
db,
internalCustomerId,
@@ -106,7 +87,7 @@ export const getCusInvoices = async ({
invoice: i,
withItems,
features,
}),
})
);
return processedInvoices;
@@ -140,7 +121,7 @@ export const getCusEntsInFeatures = async ({
if (internalFeatureIds) {
cusEnts = cusEnts.filter((cusEnt) =>
internalFeatureIds.includes(cusEnt.internal_feature_id),
internalFeatureIds.includes(cusEnt.internal_feature_id)
);
}
@@ -149,7 +130,7 @@ export const getCusEntsInFeatures = async ({
cusEnts = cusEnts.filter(
(cusEnt) =>
nullish(cusEnt.customer_product.internal_entity_id) ||
cusEnt.customer_product.internal_entity_id === entity.internal_id,
cusEnt.customer_product.internal_entity_id === entity.internal_id
// || cusEnt.entities
);
}

View File

@@ -17,6 +17,8 @@
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"incremental": false,
"noEmitOnError": false,
"paths": {
"@/*": ["src/*"],
"@emails/*": ["emails/*"]

View File

@@ -131,6 +131,7 @@ export * from "./models/attachModels/attachPreviewModels.js";
export * from "./models/attachModels/attachEnums/AttachBranch.js";
export * from "./models/attachModels/attachEnums/AttachFunction.js";
export * from "./models/attachModels/attachEnums/AttachConfig.js";
export * from "./models/attachModels/attachBody.js";
// Utils
export * from "./utils/displayUtils.js";

View File

@@ -1,12 +1,8 @@
import { notNullish } from "@/utils/genUtils.js";
import {
CreateFreeTrialSchema,
FreeTrialSchema,
ProductItemSchema,
} from "@autumn/shared";
import { FeatureOptionsSchema } from "@autumn/shared";
import { z } from "zod";
import { FeatureOptionsSchema } from "../cusProductModels/cusProductModels.js";
import { ProductItemSchema } from "../productV2Models/productItemModels/productItemModels.js";
import { CreateFreeTrialSchema } from "../productModels/freeTrialModels/freeTrialModels.js";
import { notNullish } from "../../utils/utils.js";
export const AttachBodySchema = z
.object({
@@ -58,7 +54,7 @@ export const AttachBodySchema = z
},
{
message: "`product_id` is not provided",
},
}
)
.refine(
(data) => {
@@ -70,7 +66,7 @@ export const AttachBodySchema = z
},
{
message: "provide either one `product_id` or `product_ids`",
},
}
)
.refine(
(data) => {
@@ -85,7 +81,7 @@ export const AttachBodySchema = z
},
{
message: "Can't pass in duplicate product_ids",
},
}
)
.refine(
(data) => {
@@ -97,7 +93,7 @@ export const AttachBodySchema = z
},
{
message: "Can't pass in product_ids if is_custom is true",
},
}
)
.refine(
(data) => {
@@ -109,20 +105,7 @@ export const AttachBodySchema = z
},
{
message: "Can't pass in items if is_custom is false",
},
}
);
// .refine(
// (data) => {
// if (data.free_trial && !data.is_custom) {
// return false;
// }
// return true;
// },
// {
// message: "Can't pass in free_trial if is_custom is false",
// },
// );
export type AttachBody = z.infer<typeof AttachBodySchema>;

1
shared/utils/utils.ts Normal file
View File

@@ -0,0 +1 @@
export const notNullish = (value: any) => value !== null && value !== undefined;

View File

@@ -1,351 +0,0 @@
import * as React from "react"
import * as RechartsPrimitive from "recharts"
import { cn } from "@/lib/utils"
// Format: { THEME_NAME: CSS_SELECTOR }
const THEMES = { light: "", dark: ".dark" } as const
export type ChartConfig = {
[k in string]: {
label?: React.ReactNode
icon?: React.ComponentType
} & (
| { color?: string; theme?: never }
| { color?: never; theme: Record<keyof typeof THEMES, string> }
)
}
type ChartContextProps = {
config: ChartConfig
}
const ChartContext = React.createContext<ChartContextProps | null>(null)
function useChart() {
const context = React.useContext(ChartContext)
if (!context) {
throw new Error("useChart must be used within a <ChartContainer />")
}
return context
}
function ChartContainer({
id,
className,
children,
config,
...props
}: React.ComponentProps<"div"> & {
config: ChartConfig
children: React.ComponentProps<
typeof RechartsPrimitive.ResponsiveContainer
>["children"]
}) {
const uniqueId = React.useId()
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
return (
<ChartContext.Provider value={{ config }}>
<div
data-slot="chart"
data-chart={chartId}
className={cn(
"[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
className
)}
{...props}
>
<ChartStyle id={chartId} config={config} />
<RechartsPrimitive.ResponsiveContainer>
{children}
</RechartsPrimitive.ResponsiveContainer>
</div>
</ChartContext.Provider>
)
}
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
const colorConfig = Object.entries(config).filter(
([, config]) => config.theme || config.color
)
if (!colorConfig.length) {
return null
}
return (
<style
dangerouslySetInnerHTML={{
__html: Object.entries(THEMES)
.map(
([theme, prefix]) => `
${prefix} [data-chart=${id}] {
${colorConfig
.map(([key, itemConfig]) => {
const color =
itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
itemConfig.color
return color ? ` --color-${key}: ${color};` : null
})
.join("\n")}
}
`
)
.join("\n"),
}}
/>
)
}
const ChartTooltip = RechartsPrimitive.Tooltip
function ChartTooltipContent({
active,
payload,
className,
indicator = "dot",
hideLabel = false,
hideIndicator = false,
label,
labelFormatter,
labelClassName,
formatter,
color,
nameKey,
labelKey,
}: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
React.ComponentProps<"div"> & {
hideLabel?: boolean
hideIndicator?: boolean
indicator?: "line" | "dot" | "dashed"
nameKey?: string
labelKey?: string
}) {
const { config } = useChart()
const tooltipLabel = React.useMemo(() => {
if (hideLabel || !payload?.length) {
return null
}
const [item] = payload
const key = `${labelKey || item?.dataKey || item?.name || "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
const value =
!labelKey && typeof label === "string"
? config[label as keyof typeof config]?.label || label
: itemConfig?.label
if (labelFormatter) {
return (
<div className={cn("font-medium", labelClassName)}>
{labelFormatter(value, payload)}
</div>
)
}
if (!value) {
return null
}
return <div className={cn("font-medium", labelClassName)}>{value}</div>
}, [
label,
labelFormatter,
payload,
hideLabel,
labelClassName,
config,
labelKey,
])
if (!active || !payload?.length) {
return null
}
const nestLabel = payload.length === 1 && indicator !== "dot"
return (
<div
className={cn(
"border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl",
className
)}
>
{!nestLabel ? tooltipLabel : null}
<div className="grid gap-1.5">
{payload.map((item, index) => {
const key = `${nameKey || item.name || item.dataKey || "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
const indicatorColor = color || item.payload.fill || item.color
return (
<div
key={item.dataKey}
className={cn(
"[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
indicator === "dot" && "items-center"
)}
>
{formatter && item?.value !== undefined && item.name ? (
formatter(item.value, item.name, item, index, item.payload)
) : (
<>
{itemConfig?.icon ? (
<itemConfig.icon />
) : (
!hideIndicator && (
<div
className={cn(
"shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
{
"h-2.5 w-2.5": indicator === "dot",
"w-1": indicator === "line",
"w-0 border-[1.5px] border-dashed bg-transparent":
indicator === "dashed",
"my-0.5": nestLabel && indicator === "dashed",
}
)}
style={
{
"--color-bg": indicatorColor,
"--color-border": indicatorColor,
} as React.CSSProperties
}
/>
)
)}
<div
className={cn(
"flex flex-1 justify-between leading-none",
nestLabel ? "items-end" : "items-center"
)}
>
<div className="grid gap-1.5">
{nestLabel ? tooltipLabel : null}
<span className="text-muted-foreground">
{itemConfig?.label || item.name}
</span>
</div>
{item.value && (
<span className="text-foreground font-mono font-medium tabular-nums">
{item.value.toLocaleString()}
</span>
)}
</div>
</>
)}
</div>
)
})}
</div>
</div>
)
}
const ChartLegend = RechartsPrimitive.Legend
function ChartLegendContent({
className,
hideIcon = false,
payload,
verticalAlign = "bottom",
nameKey,
}: React.ComponentProps<"div"> &
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
hideIcon?: boolean
nameKey?: string
}) {
const { config } = useChart()
if (!payload?.length) {
return null
}
return (
<div
className={cn(
"flex items-center justify-center gap-4",
verticalAlign === "top" ? "pb-3" : "pt-3",
className
)}
>
{payload.map((item) => {
const key = `${nameKey || item.dataKey || "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
return (
<div
key={item.value}
className={cn(
"[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3"
)}
>
{itemConfig?.icon && !hideIcon ? (
<itemConfig.icon />
) : (
<div
className="h-2 w-2 shrink-0 rounded-[2px]"
style={{
backgroundColor: item.color,
}}
/>
)}
{itemConfig?.label}
</div>
)
})}
</div>
)
}
// Helper to extract item config from a payload.
function getPayloadConfigFromPayload(
config: ChartConfig,
payload: unknown,
key: string
) {
if (typeof payload !== "object" || payload === null) {
return undefined
}
const payloadPayload =
"payload" in payload &&
typeof payload.payload === "object" &&
payload.payload !== null
? payload.payload
: undefined
let configLabelKey: string = key
if (
key in payload &&
typeof payload[key as keyof typeof payload] === "string"
) {
configLabelKey = payload[key as keyof typeof payload] as string
} else if (
payloadPayload &&
key in payloadPayload &&
typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
) {
configLabelKey = payloadPayload[
key as keyof typeof payloadPayload
] as string
}
return configLabelKey in config
? config[configLabelKey]
: config[key as keyof typeof config]
}
export {
ChartContainer,
ChartTooltip,
ChartTooltipContent,
ChartLegend,
ChartLegendContent,
ChartStyle,
}

View File

@@ -7,7 +7,6 @@ import { AttachModal } from "./AttachModal";
import { useAxiosInstance } from "@/services/useAxiosInstance";
import { getBackendErr } from "@/utils/genUtils";
import { toast } from "sonner";
import { FeatureOptions, ProductV2 } from "@autumn/shared";
import { getAttachBody } from "./attachProductUtils";
export const AttachButton = () => {
@@ -32,7 +31,7 @@ export const AttachButton = () => {
product,
entityId,
version: version || product.version,
}),
})
);
setPreview(res.data);