fix: moved attach body to shared folder to fix zod error
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
when importing db, or client, it should look like this:
|
||||
|
||||
import { db } from "@/db/initDrizzle.js";
|
||||
@@ -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
24
pnpm-lock.yaml
generated
@@ -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:
|
||||
|
||||
24
server/src/external/autumn/autumnCli.ts
vendored
24
server/src/external/autumn/autumnCli.ts
vendored
@@ -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;
|
||||
},
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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 ({
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"incremental": false,
|
||||
"noEmitOnError": false,
|
||||
"paths": {
|
||||
"@/*": ["src/*"],
|
||||
"@emails/*": ["emails/*"]
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
1
shared/utils/utils.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const notNullish = (value: any) => value !== null && value !== undefined;
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user