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:
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}: {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user