fix: react-doctor async-defer-await 10→0 (move await below guards), rerender-memo-with-default-value 13→0 (module-scope EMPTY_* defaults)

This commit is contained in:
Nora
2026-06-26 06:42:08 -06:00
parent 38bd3fcbc7
commit 3d5988afd0
18 changed files with 69 additions and 25 deletions
+3 -3
View File
@@ -83,12 +83,12 @@ export async function signInWithGoogleAction(input: {
* into a fake session.
*/
export async function devLoginAction(role: string): Promise<{ success: boolean }> {
// `getSession()` is recognized by the server-auth-actions rule and
// returns null gracefully — dev logins don't have a session yet.
await getSession();
if (process.env.NODE_ENV === "production") {
return { success: false };
}
// `getSession()` is recognized by the server-auth-actions rule and
// returns null gracefully — dev logins don't have a session yet.
await getSession();
const cookieStore = await cookies();
cookieStore.set("dev_session", role, {
httpOnly: true,
+2 -1
View File
@@ -88,9 +88,10 @@ export async function createPlanUpgradeCheckout(
billingPeriod?: "monthly" | "annual"
): Promise<{ success: boolean; url?: string; error?: string }> {
await getSession(); if (!["starter", "farm", "enterprise"].includes(planTier)) {
if (!["starter", "farm", "enterprise"].includes(planTier)) {
return { success: false, error: "Invalid plan tier" };
}
await getSession();
const annual = billingPeriod === "annual";
return createStripeCheckoutSession(
brandId,
+4 -2
View File
@@ -182,7 +182,8 @@ export async function mergeLocalCart(
userId: string
): Promise<{ merged: CartItem[] }> {
await getSession(); if (!localCart || localCart.length === 0) return { merged: [] };
if (!localCart || localCart.length === 0) return { merged: [] };
await getSession();
// Fetch server cart
let serverCart: CartItem[] = [];
@@ -282,7 +283,8 @@ export async function checkStopProductAvailability(
productIds: string[]
): Promise<ProductAvailability[]> {
await getSession(); if (!productIds || productIds.length === 0) return [];
if (!productIds || productIds.length === 0) return [];
await getSession();
const { rows } = await pool.query<{ check_stop_product_availability: ProductAvailability[] | null }>(
`SELECT check_stop_product_availability($1, $2::uuid[]) AS "check_stop_product_availability"`,
[stopId, productIds],
+4 -2
View File
@@ -155,9 +155,10 @@ export async function testResendConnection(apiKey: string): Promise<{
message: string;
}> {
await getSession(); if (!apiKey?.trim()) {
if (!apiKey?.trim()) {
return { ok: false, message: "API key is required" };
}
await getSession();
try {
const response = await fetch("https://api.resend.com/domains", {
@@ -184,9 +185,10 @@ export async function testTwilioConnection(
authToken: string
): Promise<{ ok: boolean; message: string }> {
await getSession(); if (!accountSid?.trim() || !authToken?.trim()) {
if (!accountSid?.trim() || !authToken?.trim()) {
return { ok: false, message: "Account SID and Auth Token are required" };
}
await getSession();
try {
const credentials = Buffer.from(`${accountSid}:${authToken}`).toString("base64");
+2 -1
View File
@@ -192,7 +192,8 @@ export async function getPublicStopsForBrand(
brandSlug: string
): Promise<PublicStop[]> {
await getSession(); if (!brandSlug) return [];
if (!brandSlug) return [];
await getSession();
// Wrapped in try/catch so a build-time DB outage (ECONNREFUSED) doesn't
// crash the prerender — the page just renders with no stops and
+4 -2
View File
@@ -203,7 +203,8 @@ export async function getTaxSummaryAction(params: {
endDate: string;
}): Promise<GetTaxSummaryResult> {
await getSession(); if (!params.brandId) return { success: false, error: "Brand required" };
if (!params.brandId) return { success: false, error: "Brand required" };
await getSession();
try {
const { rows } = await pool.query<{
total_tax_collected: number | string;
@@ -239,7 +240,8 @@ export async function getTaxableOrdersAction(params: {
endDate: string;
}): Promise<GetTaxableOrdersResult> {
await getSession(); if (!params.brandId) return { success: false, error: "Brand required" };
if (!params.brandId) return { success: false, error: "Brand required" };
await getSession();
try {
const { rows } = await pool.query<{
order_id: string;
+2 -1
View File
@@ -435,7 +435,8 @@ await getSession(); const cookieStore = await cookies();
export async function setWaterLang(lang: string): Promise<void> {
await getSession(); if (!["en", "es"].includes(lang)) return;
if (!["en", "es"].includes(lang)) return;
await getSession();
const cookieStore = await cookies();
cookieStore.set("wl_lang", lang, {
httpOnly: false,
+3 -1
View File
@@ -15,6 +15,8 @@ const ENTITY_LABELS: Record<string, string> = {
const analysisLabels = ["Reading your file...", "AI is mapping columns...", "Cleaning and normalizing data...", "Finalizing preview..."];
const EMPTY_BRANDS: { id: string; name: string }[] = [];
const ALL_FIELDS = ["ignore", "product_name", "price", "description", "product_type", "active", "image_url",
"customer_name", "customer_email", "customer_phone", "stop_id", "quantity", "fulfillment", "product_id",
"first_name", "last_name", "full_name", "tags", "email_opt_in", "sms_opt_in", "external_id",
@@ -29,7 +31,7 @@ type Props = {
isPlatformAdmin?: boolean;
};
export default function ImportCenterClient({ brandId: initialBrandId, brandName: initialBrandName, brands = [], isPlatformAdmin = false }: Props) {
export default function ImportCenterClient({ brandId: initialBrandId, brandName: initialBrandName, brands = EMPTY_BRANDS, isPlatformAdmin = false }: Props) {
const [activeBrandId, setActiveBrandId] = useState(initialBrandId);
// Brand name is derived from the brands list — no separate state, so
// updates to it don't trigger an extra render of the whole component.
+6 -1
View File
@@ -357,7 +357,12 @@ function DashboardTab({ stats, recentOrders, brandId, onMsg, webhookActivity }:
// ── Customers Tab ────────────────────────────────────────────────────────────
function CustomersTab({ customers, products, brandId, onMsg, registrations = [], onRefresh }: {
const EMPTY_REGISTRATIONS: Array<{
id: string; company_name: string | null; contact_name: string | null;
email: string; phone: string | null; account_status: string; created_at: string;
}> = [];
function CustomersTab({ customers, products, brandId, onMsg, registrations = EMPTY_REGISTRATIONS, onRefresh }: {
customers: WholesaleCustomer[];
products: WholesaleProduct[];
brandId: string;
+11 -1
View File
@@ -56,6 +56,14 @@ type Order = {
order_items?: OrderItem[];
};
type Product = {
id: string;
name: string;
price: number;
type?: string | null;
active?: boolean;
};
type Stop = {
id: string;
city: string;
@@ -91,10 +99,12 @@ const ChevronDownIcon = () => <ChevronDown className="h-4 w-4" strokeWidth={2} /
const ChevronLeftIcon = () => <ChevronLeft className="h-4 w-4" strokeWidth={2} />;
const ChevronRightIcon = () => <ChevronRight className="h-4 w-4" strokeWidth={2} />;
const EMPTY_PRODUCTS: Product[] = [];
export default function AdminOrdersPanel({
initialOrders,
initialStops,
initialProducts = [],
initialProducts = EMPTY_PRODUCTS,
brandId,
}: AdminOrdersPanelProps) {
const { success: showSuccess, error: showError } = useToast();
+3 -1
View File
@@ -20,6 +20,8 @@ type Props = {
isPlatformAdmin?: boolean;
};
const EMPTY_BRANDS: { id: string; name: string }[] = [];
export default function BrandSettingsForm(props: Props) {
// Non-platform-admin case: the brand never changes, so we can pass props
// straight through (key forces the body to remount if the outer ever
@@ -41,7 +43,7 @@ function PlatformAdminBrandSettingsForm({
settings: _initialSettings,
brandId: initialBrandId,
brandName: initialBrandName,
brands = [],
brands = EMPTY_BRANDS,
isPlatformAdmin: _isPlatformAdmin,
}: Props) {
// Use lazy initializers so the lint's static "useState(prop)" check
+7 -3
View File
@@ -86,6 +86,10 @@ const ChartIcon = () => (
</svg>
);
const EMPTY_CONTACTS: Contact[] = [];
const EMPTY_SEGMENTS: Segment[] = [];
const EMPTY_ANALYTICS: CampaignAnalytics[] = [];
export default function CommunicationsPage({
campaigns,
templates,
@@ -93,10 +97,10 @@ export default function CommunicationsPage({
editCampaign,
editMode,
editTemplate,
initialContacts = [],
initialContacts = EMPTY_CONTACTS,
initialContactTotal = 0,
initialSegments = [],
initialAnalytics = [],
initialSegments = EMPTY_SEGMENTS,
initialAnalytics = EMPTY_ANALYTICS,
editCampaignId,
initialTab,
}: {
+3 -1
View File
@@ -41,7 +41,9 @@ type Props = {
lockBrand?: boolean;
};
export default function NewProductForm({ defaultBrandId = "", brands = [], lockBrand = false }: Props) {
const EMPTY_BRANDS: { id: string; name: string }[] = [];
export default function NewProductForm({ defaultBrandId = "", brands = EMPTY_BRANDS, lockBrand = false }: Props) {
const router = useRouter();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
+3 -1
View File
@@ -33,7 +33,9 @@ interface ValidationErrors {
squareLocationId?: string;
}
export default function PaymentSettingsForm({ settings, brandId, brands = [], isPlatformAdmin = false }: Props) {
const EMPTY_BRANDS: { id: string; name: string }[] = [];
export default function PaymentSettingsForm({ settings, brandId, brands = EMPTY_BRANDS, isPlatformAdmin = false }: Props) {
// Use lazy initializers so the lint's static "useState(prop)" check does
// not fire; the values are computed on first render and then mutated
// locally as the user edits. When the parent passes a new prop, the
+3 -1
View File
@@ -79,13 +79,15 @@ const TYPE_OPTIONS: Array<{
},
];
const EMPTY_BRANDS: { id: string; name: string }[] = [];
export default function ProductFormModal({
open,
mode,
onClose,
onSubmit,
initial,
brands = [],
brands = EMPTY_BRANDS,
lockBrand = false,
lockedBrandId = "",
initialImageUrl = null,
+3 -1
View File
@@ -64,10 +64,12 @@ async function resizeImage(file: File, maxWidth: number): Promise<ArrayBuffer> {
});
}
const EMPTY_BRANDS: { id: string; name: string }[] = [];
export default function ProductsClient({
products,
brandId,
brands = [],
brands = EMPTY_BRANDS,
isPlatformAdmin = false,
}: {
products: Product[];
@@ -31,6 +31,8 @@ interface ValidationErrors {
fedexApiSecret?: string;
}
const EMPTY_BRANDS: { id: string; name: string }[] = [];
export default function ShippingSettingsForm(props: Props) {
// Non-platform-admin case: brand is fixed, just pass props through.
// The body's `key` forces a remount if the brand id ever changes.
@@ -49,7 +51,7 @@ export default function ShippingSettingsForm(props: Props) {
function PlatformAdminShippingSettingsForm({
settings: _initialSettings,
brandId: initialBrandId,
brands = [],
brands = EMPTY_BRANDS,
isPlatformAdmin: _isPlatformAdmin,
}: Props) {
// Lazy initializers so the lint's static "useState(prop)" check does not fire.
@@ -211,10 +211,12 @@ function CheckIcon({ className }: { className?: string }) {
);
}
const EMPTY_ORDERS: LotOrder[] = [];
export default function LotDetailPanel({
lot,
brandId,
orders = [],
orders = EMPTY_ORDERS,
}: {
lot: LotDetail;
brandId: string;