diff --git a/vite/src/hooks/common/useOrg.tsx b/vite/src/hooks/common/useOrg.tsx index 131fb2147..4b516cbbd 100644 --- a/vite/src/hooks/common/useOrg.tsx +++ b/vite/src/hooks/common/useOrg.tsx @@ -4,23 +4,12 @@ import { useEffect } from "react"; import { authClient, useListOrganizations } from "@/lib/auth-client"; import { useAxiosInstance } from "@/services/useAxiosInstance"; -const ORG_STORAGE_KEY = "autumn_org"; - let lastSwitchedOrgId: string | null = null; export const setLastSwitchedOrgId = (id: string) => { lastSwitchedOrgId = id; }; export const getLastSwitchedOrgId = () => lastSwitchedOrgId; -/** Clears all org-related localStorage cache entries. Call before reload on session changes (impersonation start/stop). */ -export const clearOrgCache = () => { - for (const key of Object.keys(localStorage)) { - if (key.startsWith(ORG_STORAGE_KEY)) { - localStorage.removeItem(key); - } - } -}; - export const useOrg = (params?: { env?: AppEnv }) => { const axiosInstance = useAxiosInstance({ env: params?.env }); const { data: orgList } = useListOrganizations(); @@ -28,32 +17,12 @@ export const useOrg = (params?: { env?: AppEnv }) => { const fetcher = async () => { try { const { data } = await axiosInstance.get("/organization"); - if (data) { - const storageKey = params?.env - ? `${ORG_STORAGE_KEY}_${params.env}` - : ORG_STORAGE_KEY; - localStorage.setItem(storageKey, JSON.stringify(data)); - } return data; } catch { return null; } }; - const getInitialData = () => { - try { - const storageKey = params?.env - ? `${ORG_STORAGE_KEY}_${params.env}` - : ORG_STORAGE_KEY; - const stored = localStorage.getItem(storageKey); - return stored ? JSON.parse(stored) : undefined; - } catch { - return undefined; - } - }; - - const initialDataValue = getInitialData(); - const { data: org, isLoading, @@ -62,7 +31,6 @@ export const useOrg = (params?: { env?: AppEnv }) => { } = useQuery({ queryKey: params?.env ? ["org", params.env] : ["org"], queryFn: fetcher, - initialData: initialDataValue, placeholderData: keepPreviousData, refetchOnWindowFocus: true, staleTime: 30_000, @@ -78,10 +46,10 @@ export const useOrg = (params?: { env?: AppEnv }) => { } else { console.log("No org to set active, signing out"); await authClient.signOut(); + window.location.href = "/sign-in"; } }; - // 1. If no org... if (!org && !isLoading) { handleNoActiveOrg(); } diff --git a/vite/src/views/admin/AdminView.tsx b/vite/src/views/admin/AdminView.tsx index 336f7a5d2..68b085cb7 100644 --- a/vite/src/views/admin/AdminView.tsx +++ b/vite/src/views/admin/AdminView.tsx @@ -2,7 +2,6 @@ import { Globe } from "@phosphor-icons/react"; import { useNavigate } from "react-router"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; -import { clearOrgCache } from "@/hooks/common/useOrg"; import { authClient } from "@/lib/auth-client"; import { AdminOrgTable } from "@/views/admin/AdminOrgTable"; import { AdminUserTable } from "@/views/admin/AdminUserTable"; @@ -35,7 +34,6 @@ export const AdminView = () => { return; } - clearOrgCache(); window.location.reload(); }; diff --git a/vite/src/views/admin/ImpersonateRedirect.tsx b/vite/src/views/admin/ImpersonateRedirect.tsx index 779eecd42..6a6b630fe 100644 --- a/vite/src/views/admin/ImpersonateRedirect.tsx +++ b/vite/src/views/admin/ImpersonateRedirect.tsx @@ -2,7 +2,6 @@ import { AlertCircle, Loader2, ShieldCheck } from "lucide-react"; import { useEffect, useState } from "react"; import { useNavigate, useSearchParams } from "react-router"; import { Button } from "@/components/v2/buttons/Button"; -import { clearOrgCache } from "@/hooks/common/useOrg"; import { authClient } from "@/lib/auth-client"; import { useAxiosInstance } from "../../services/useAxiosInstance"; import { useAdmin } from "./hooks/useAdmin"; @@ -65,9 +64,6 @@ export function ImpersonateRedirect() { // Step 5: Navigate to the redirect path setStatus("Redirecting..."); - - // Clear stale org cache before navigating so the new session loads fresh data - clearOrgCache(); window.location.href = redirect; } catch (err: unknown) { const errorMessage = diff --git a/vite/src/views/admin/adminUtils.ts b/vite/src/views/admin/adminUtils.ts index 102c54174..534de8030 100644 --- a/vite/src/views/admin/adminUtils.ts +++ b/vite/src/views/admin/adminUtils.ts @@ -10,7 +10,6 @@ import type { Rollover, } from "@autumn/shared"; import { toast } from "sonner"; -import { clearOrgCache } from "@/hooks/common/useOrg"; import { authClient } from "@/lib/auth-client"; import { formatUnixToDate } from "../../utils/formatUtils/formatDateUtils"; @@ -66,7 +65,6 @@ export const impersonateUser = async ({ await authClient.organization.setActive({ organizationId }); } - clearOrgCache(); window.location.reload(); }; diff --git a/vite/src/views/auth/Consent.tsx b/vite/src/views/auth/Consent.tsx index d3a839423..48996686e 100644 --- a/vite/src/views/auth/Consent.tsx +++ b/vite/src/views/auth/Consent.tsx @@ -1,5 +1,4 @@ import { type GroupedPermission, groupAndFormatScopes } from "@autumn/shared"; -import { clearOrgCache } from "@/hooks/common/useOrg"; import { Check, ChevronDown, @@ -147,7 +146,6 @@ export const Consent = () => { await authClient.organization.setActive({ organizationId: orgId, }); - clearOrgCache(); window.location.reload(); } catch (_) { toast.error("Failed to switch organization"); diff --git a/vite/src/views/customers2/components/sheets/AutoTopUpSection.tsx b/vite/src/views/customers2/components/sheets/AutoTopUpSection.tsx deleted file mode 100644 index eb4731684..000000000 --- a/vite/src/views/customers2/components/sheets/AutoTopUpSection.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { PurchaseLimitInterval } from "@autumn/shared"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/v2/selects/Select"; -import type { BalanceEditFormInstance } from "./useBalanceEditForm"; - -const RATE_LIMIT_INTERVALS = [ - { value: PurchaseLimitInterval.Hour, label: "Hour" }, - { value: PurchaseLimitInterval.Day, label: "Day" }, - { value: PurchaseLimitInterval.Week, label: "Week" }, - { value: PurchaseLimitInterval.Month, label: "Month" }, -]; - -export function AutoTopUpSection({ form }: { form: BalanceEditFormInstance }) { - return ( -
- - {(field) => ( - - )} - - - - {(enabledField) => - enabledField.state.value && ( - <> -
- - {(field) => ( - - )} - - - {(field) => ( - - )} - -
- - - {(field) => ( - - )} - - - - {(maxField) => - maxField.state.value && ( -
- - {(field) => ( -
-
- Interval -
-

- Rate limit reset period -

- -
- )} -
- - {(field) => ( - - )} - -
- ) - } -
- - ) - } -
-
- ); -} diff --git a/vite/src/views/customers2/components/sheets/BalanceEditSheet.tsx b/vite/src/views/customers2/components/sheets/BalanceEditSheet.tsx index d9f027239..3af988c3c 100644 --- a/vite/src/views/customers2/components/sheets/BalanceEditSheet.tsx +++ b/vite/src/views/customers2/components/sheets/BalanceEditSheet.tsx @@ -1,12 +1,9 @@ import { - type AutoTopup, computeGrantedBalanceInput, type Entity, type FullCusProduct, type FullCustomerEntitlement, type FullCustomerPrice, - isOneOffPrice, - isPrepaidPrice, isUnlimitedCusEnt, numberWithCommas, } from "@autumn/shared"; @@ -23,14 +20,12 @@ import { LabelInput } from "@/components/v2/inputs/LabelInput"; import { SheetHeader, SheetSection } from "@/components/v2/sheets/InlineSheet"; import { useCustomerBalanceSheetStore } from "@/hooks/stores/useCustomerBalanceSheetStore"; import { useSheetStore } from "@/hooks/stores/useSheetStore"; -import { CusService } from "@/services/customers/CusService"; import { useAxiosInstance } from "@/services/useAxiosInstance"; import { formatUnixToDateTime } from "@/utils/formatUtils/formatDateUtils"; import { getBackendErr, notNullish } from "@/utils/genUtils"; import { useCusQuery } from "@/views/customers/customer/hooks/useCusQuery"; import { InfoBox } from "@/views/onboarding2/integrate/components/InfoBox"; import { useCustomerContext } from "../../customer/CustomerContext"; -import { AutoTopUpSection } from "./AutoTopUpSection"; import { BalanceEditPreviews } from "./BalanceEditPreviews"; import { GrantedBalancePopover } from "./GrantedBalancePopover"; import { @@ -80,19 +75,6 @@ export function BalanceEditSheet() { cp.price.entitlement_id === selectedCusEnt.entitlement.id, ); - const hasOneOffPrepaidPrice = cusPrice - ? isOneOffPrice(cusPrice.price) && isPrepaidPrice(cusPrice.price) - : false; - const hasExistingAutoTopUp = customer?.auto_topups?.some( - (c: AutoTopup) => c.feature_id === featureId, - ); - const isEligibleForAutoTopUp = - hasOneOffPrepaidPrice || !!hasExistingAutoTopUp; - - const existingAutoTopUp = - customer?.auto_topups?.find((c: AutoTopup) => c.feature_id === featureId) ?? - null; - return (
)}
@@ -161,8 +141,6 @@ function BalanceEditForm({ cusProduct, cusPrice, featureId, - existingAutoTopUp, - isEligibleForAutoTopUp, }: { selectedCusEnt: FullCustomerEntitlement; entityId: string | null; @@ -170,13 +148,10 @@ function BalanceEditForm({ cusProduct: FullCusProduct | undefined; cusPrice: FullCustomerPrice | undefined; featureId: string; - existingAutoTopUp: AutoTopup | null; - isEligibleForAutoTopUp: boolean; }) { const form = useBalanceEditForm({ selectedCusEnt, entityId, - existingAutoTopUp, }); return ( @@ -190,7 +165,7 @@ function BalanceEditForm({ /> - + - {isEligibleForAutoTopUp && ( - - - - )} - c.feature_id !== featureId, - ); - - promises.push( - CusService.updateCustomer({ - axios: axiosInstance, - customer_id: customer.id || customer.internal_id, - data: { - billing_controls: { - auto_topups: [...otherConfigs, newConfig], - }, - }, - }), - ); - } - await Promise.all(promises); toast.success("Updated successfully"); handleClose(); @@ -630,20 +565,3 @@ function hasBalanceChanges({ ); } -function hasAutoTopUpChanges({ - form, -}: { - form: BalanceEditFormInstance; -}): boolean { - const meta = form.state.fieldMeta; - - return ( - meta["autoTopUp.enabled"]?.isDirty || - meta["autoTopUp.threshold"]?.isDirty || - meta["autoTopUp.quantity"]?.isDirty || - meta["autoTopUp.maxPurchasesEnabled"]?.isDirty || - meta["autoTopUp.interval"]?.isDirty || - meta["autoTopUp.maxPurchases"]?.isDirty || - false - ); -} diff --git a/vite/src/views/customers2/components/sheets/balanceEditFormSchema.ts b/vite/src/views/customers2/components/sheets/balanceEditFormSchema.ts index 8dc542a1b..046fe06b9 100644 --- a/vite/src/views/customers2/components/sheets/balanceEditFormSchema.ts +++ b/vite/src/views/customers2/components/sheets/balanceEditFormSchema.ts @@ -1,4 +1,3 @@ -import { PurchaseLimitInterval } from "@autumn/shared"; import { z } from "zod/v4"; export const BalanceEditFormSchema = z @@ -8,17 +7,9 @@ export const BalanceEditFormSchema = z grantedAndPurchasedBalance: z.number().nullable(), nextResetAt: z.number().nullable(), addValue: z.number().nullable(), - autoTopUp: z.object({ - enabled: z.boolean(), - threshold: z.number().min(0).nullable(), - quantity: z.number().min(1).nullable(), - maxPurchasesEnabled: z.boolean(), - interval: z.enum(PurchaseLimitInterval), - maxPurchases: z.number().min(1).nullable(), - }), }) .check((ctx) => { - const { mode, balance, addValue, autoTopUp } = ctx.value; + const { mode, balance, addValue } = ctx.value; if (mode === "set" && balance === null) { ctx.issues.push({ @@ -37,35 +28,6 @@ export const BalanceEditFormSchema = z input: addValue, }); } - - if (autoTopUp.enabled) { - if (autoTopUp.threshold === null || autoTopUp.threshold < 0) { - ctx.issues.push({ - code: "custom", - message: "Threshold must be 0 or above", - path: ["autoTopUp", "threshold"], - input: autoTopUp.threshold, - }); - } - if (autoTopUp.quantity === null || autoTopUp.quantity < 1) { - ctx.issues.push({ - code: "custom", - message: "Quantity must be 1 or above", - path: ["autoTopUp", "quantity"], - input: autoTopUp.quantity, - }); - } - if (autoTopUp.maxPurchasesEnabled) { - if (autoTopUp.maxPurchases === null || autoTopUp.maxPurchases < 1) { - ctx.issues.push({ - code: "custom", - message: "Max purchases must be 1 or above", - path: ["autoTopUp", "maxPurchases"], - input: autoTopUp.maxPurchases, - }); - } - } - } }); export type BalanceEditForm = z.infer; diff --git a/vite/src/views/customers2/components/sheets/useBalanceEditForm.ts b/vite/src/views/customers2/components/sheets/useBalanceEditForm.ts index 1e2bf4cbd..133a83263 100644 --- a/vite/src/views/customers2/components/sheets/useBalanceEditForm.ts +++ b/vite/src/views/customers2/components/sheets/useBalanceEditForm.ts @@ -1,11 +1,9 @@ import { - type AutoTopup, cusEntsToBalance, cusEntsToGrantedBalance, cusEntsToPrepaidQuantity, type FullCusEntWithFullCusProduct, nullish, - PurchaseLimitInterval, } from "@autumn/shared"; import { useAppForm } from "@/hooks/form/form"; import { @@ -16,11 +14,9 @@ import { export function useBalanceEditForm({ selectedCusEnt, entityId, - existingAutoTopUp, }: { selectedCusEnt: FullCusEntWithFullCusProduct; entityId: string | null; - existingAutoTopUp: AutoTopup | null; }) { const prepaidAllowance = cusEntsToPrepaidQuantity({ cusEnts: [selectedCusEnt], @@ -47,16 +43,6 @@ export function useBalanceEditForm({ grantedAndPurchasedBalance: grantedAndPurchasedBalance ?? null, nextResetAt: selectedCusEnt.next_reset_at ?? null, addValue: null, - autoTopUp: { - enabled: existingAutoTopUp?.enabled ?? false, - threshold: existingAutoTopUp?.threshold ?? null, - quantity: existingAutoTopUp?.quantity ?? null, - maxPurchasesEnabled: !!existingAutoTopUp?.purchase_limit, - interval: - existingAutoTopUp?.purchase_limit?.interval ?? - PurchaseLimitInterval.Month, - maxPurchases: existingAutoTopUp?.purchase_limit?.limit ?? null, - }, } as BalanceEditForm, validators: { onChange: BalanceEditFormSchema, diff --git a/vite/src/views/main-sidebar/components/AdminDropdownItems.tsx b/vite/src/views/main-sidebar/components/AdminDropdownItems.tsx index c1a1170d5..fb1fe3638 100644 --- a/vite/src/views/main-sidebar/components/AdminDropdownItems.tsx +++ b/vite/src/views/main-sidebar/components/AdminDropdownItems.tsx @@ -5,7 +5,6 @@ import { DropdownMenuItem, DropdownMenuSeparator, } from "@/components/ui/dropdown-menu"; -import { clearOrgCache } from "@/hooks/common/useOrg"; import { authClient, useSession } from "@/lib/auth-client"; import { getBackendErr, notNullish } from "@/utils/genUtils"; import { AdminOnly } from "@/views/admin/components/AdminOnly"; @@ -27,7 +26,6 @@ export const AdminDropdownItems = () => { setStopImpersonatingLoading(true); try { await authClient.admin.stopImpersonating(); - clearOrgCache(); window.location.reload(); } catch (error) { toast.error(getBackendErr(error, "Failed to stop impersonation")); diff --git a/vite/src/views/main-sidebar/components/LogOutItem.tsx b/vite/src/views/main-sidebar/components/LogOutItem.tsx index cc5513866..93d0da389 100644 --- a/vite/src/views/main-sidebar/components/LogOutItem.tsx +++ b/vite/src/views/main-sidebar/components/LogOutItem.tsx @@ -1,7 +1,6 @@ import { LogOut } from "lucide-react"; import React from "react"; import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; -import { clearOrgCache } from "@/hooks/common/useOrg"; import { authClient } from "@/lib/auth-client"; export const LogOutItem = () => { @@ -11,10 +10,10 @@ export const LogOutItem = () => { onClick={async () => { try { await authClient.signOut(); - clearOrgCache(); - window.location.href = "/sign-in"; } catch (error) { console.error("Error signing out:", error); + } finally { + window.location.href = "/sign-in"; } }} > diff --git a/vite/src/views/main-sidebar/components/OrgDropdown.tsx b/vite/src/views/main-sidebar/components/OrgDropdown.tsx index e680bfe8a..223627447 100644 --- a/vite/src/views/main-sidebar/components/OrgDropdown.tsx +++ b/vite/src/views/main-sidebar/components/OrgDropdown.tsx @@ -27,11 +27,7 @@ import { } from "@/components/ui/dropdown-menu"; import { Skeleton } from "@/components/ui/skeleton"; import { useTheme } from "@/contexts/ThemeProvider"; -import { - clearOrgCache, - setLastSwitchedOrgId, - useOrg, -} from "@/hooks/common/useOrg"; +import { setLastSwitchedOrgId, useOrg } from "@/hooks/common/useOrg"; import { authClient, useListOrganizations, @@ -257,8 +253,6 @@ export const useOrgSwitch = () => { organizationId: orgId, }); - clearOrgCache(); - const { data: newOrg } = await axiosInstance.get("/organization"); if (newOrg?.id) setLastSwitchedOrgId(newOrg.id);