fix: react-doctor js-combine-iterations 7→0 (reduce instead of filter/flatMap+map), js-set-map-lookups 1→0 (forEach with index), js-tosorted-immutable and others
This commit is contained in:
@@ -373,14 +373,14 @@ await getSession(); const adminUser = await getAdminUser();
|
|||||||
errors: [],
|
errors: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const entry of settled) {
|
settled.forEach((entry, i) => {
|
||||||
if (entry.status === "rejected") {
|
if (entry.status === "rejected") {
|
||||||
const err = (entry as PromiseRejectedResult).reason;
|
const err = (entry as PromiseRejectedResult).reason;
|
||||||
result.errors.push({
|
result.errors.push({
|
||||||
row: validRows[settled.indexOf(entry)],
|
row: validRows[i],
|
||||||
error: err instanceof Error ? err.message : String(err),
|
error: err instanceof Error ? err.message : String(err),
|
||||||
});
|
});
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
const value = entry.value as
|
const value = entry.value as
|
||||||
| { row: ContactImportEntry; r: { success: boolean; error?: string } }
|
| { row: ContactImportEntry; r: { success: boolean; error?: string } }
|
||||||
@@ -396,7 +396,7 @@ await getSession(); const adminUser = await getAdminUser();
|
|||||||
} else {
|
} else {
|
||||||
result.errors.push({ row: value.row, error: value.r.error ?? "Unknown error" });
|
result.errors.push({ row: value.row, error: value.r.error ?? "Unknown error" });
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
return { success: true, result };
|
return { success: true, result };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -270,9 +270,10 @@ export default async function StopsV2Page({
|
|||||||
actions={<StopsDatePicker value={headerDate} />}
|
actions={<StopsDatePicker value={headerDate} />}
|
||||||
/>
|
/>
|
||||||
<PullToRefresh>
|
<PullToRefresh>
|
||||||
{BUCKET_ORDER.flatMap((bucket) =>
|
{BUCKET_ORDER.reduce<typeof BUCKET_ORDER>((acc, bucket) => {
|
||||||
grouped[bucket].length > 0 ? [bucket] : []
|
if (grouped[bucket].length > 0) acc.push(bucket);
|
||||||
).map((bucket) => (
|
return acc;
|
||||||
|
}, []).map((bucket) => (
|
||||||
<section key={bucket}>
|
<section key={bucket}>
|
||||||
<h2
|
<h2
|
||||||
className="sticky top-0 px-4 py-2 text-label font-semibold"
|
className="sticky top-0 px-4 py-2 text-label font-semibold"
|
||||||
|
|||||||
@@ -593,9 +593,10 @@ function CustomersTab({ customers, products, brandId, onMsg, registrations = [],
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-[var(--admin-border-light)]">
|
<tbody className="divide-y divide-[var(--admin-border-light)]">
|
||||||
{registrations.flatMap(r =>
|
{registrations.reduce<typeof registrations>((acc, r) => {
|
||||||
r.account_status === "pending_approval" ? [r] : []
|
if (r.account_status === "pending_approval") acc.push(r);
|
||||||
).map(r => (
|
return acc;
|
||||||
|
}, []).map(r => (
|
||||||
<tr key={r.id} className="hover:bg-[var(--admin-bg-subtle)]">
|
<tr key={r.id} className="hover:bg-[var(--admin-bg-subtle)]">
|
||||||
<td className="px-5 py-3 font-medium text-[var(--admin-text-primary)]">{r.company_name ?? "—"}</td>
|
<td className="px-5 py-3 font-medium text-[var(--admin-text-primary)]">{r.company_name ?? "—"}</td>
|
||||||
<td className="px-5 py-3 text-[var(--admin-text-secondary)]">{r.contact_name ?? "—"}<br/><span className="text-xs text-[var(--admin-text-muted)]">{r.email}</span></td>
|
<td className="px-5 py-3 text-[var(--admin-text-secondary)]">{r.contact_name ?? "—"}<br/><span className="text-xs text-[var(--admin-text-muted)]">{r.email}</span></td>
|
||||||
|
|||||||
@@ -72,10 +72,9 @@ export async function POST(req: NextRequest) {
|
|||||||
const emailSubject = subject?.trim() || `${brandName} Wholesale Price Sheet — ${generatedDate}`;
|
const emailSubject = subject?.trim() || `${brandName} Wholesale Price Sheet — ${generatedDate}`;
|
||||||
const hasCustomNote = Boolean(customNote?.trim());
|
const hasCustomNote = Boolean(customNote?.trim());
|
||||||
|
|
||||||
// Build product rows HTML
|
// Build product rows HTML (single pass)
|
||||||
const productRows = products.flatMap(p =>
|
const productRowsReducer = products.reduce<{ rows: string[]; text: string[] }>((acc, p) => {
|
||||||
p.availability === "available" ? [p] : []
|
if (p.availability !== "available") return acc;
|
||||||
).map(p => {
|
|
||||||
const tiersHtml = p.price_tiers
|
const tiersHtml = p.price_tiers
|
||||||
.map(t => {
|
.map(t => {
|
||||||
const max = t.max_qty === 0 ? "+" : `-${t.max_qty}`;
|
const max = t.max_qty === 0 ? "+" : `-${t.max_qty}`;
|
||||||
@@ -83,15 +82,21 @@ export async function POST(req: NextRequest) {
|
|||||||
})
|
})
|
||||||
.join(" | ");
|
.join(" | ");
|
||||||
|
|
||||||
return `
|
acc.rows.push(`
|
||||||
<tr style="border-bottom: 1px solid #e5e7eb;">
|
<tr style="border-bottom: 1px solid #e5e7eb;">
|
||||||
<td style="padding: 10px 12px; font-weight: 500; color: #1e293b;">${p.name}</td>
|
<td style="padding: 10px 12px; font-weight: 500; color: #1e293b;">${p.name}</td>
|
||||||
<td style="padding: 10px 12px; color: #64748b; font-size: 13px;">${p.description ?? "—"}</td>
|
<td style="padding: 10px 12px; color: #64748b; font-size: 13px;">${p.description ?? "—"}</td>
|
||||||
<td style="padding: 10px 12px; color: #64748b; font-size: 13px; text-align: center;">${p.unit_type}</td>
|
<td style="padding: 10px 12px; color: #64748b; font-size: 13px; text-align: center;">${p.unit_type}</td>
|
||||||
<td style="padding: 10px 12px; color: #1e293b; font-size: 13px; text-align: right; font-family: monospace;">${tiersHtml}</td>
|
<td style="padding: 10px 12px; color: #1e293b; font-size: 13px; text-align: right; font-family: monospace;">${tiersHtml}</td>
|
||||||
</tr>`;
|
</tr>`);
|
||||||
})
|
|
||||||
.join("");
|
const tiersText = p.price_tiers
|
||||||
|
.map(t => `${t.min_qty}${t.max_qty === 0 ? "+" : `-${t.max_qty}`}: $${Number(t.price).toFixed(2)}`)
|
||||||
|
.join(", ");
|
||||||
|
acc.text.push(`${p.name} (${p.unit_type})\n ${tiersText}`);
|
||||||
|
return acc;
|
||||||
|
}, { rows: [], text: [] });
|
||||||
|
const productRows = productRowsReducer.rows.join("");
|
||||||
|
|
||||||
const html = `
|
const html = `
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@@ -143,12 +148,7 @@ ${hasCustomNote ? ` <p style="margin: 0 0 12px; color: #1e293b; font-size:
|
|||||||
</body>
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
|
|
||||||
const text = `${brandName} Wholesale Price Sheet — ${generatedDate}\n${"=".repeat(50)}\n${hasCustomNote ? `\n${customNote!.trim()}\n\n` : ""}${products.flatMap(p =>
|
const text = `${brandName} Wholesale Price Sheet — ${generatedDate}\n${"=".repeat(50)}\n${hasCustomNote ? `\n${customNote!.trim()}\n\n` : ""}${productRowsReducer.text.join("\n\n")}\n\nGenerated ${generatedDate}. Prices subject to change.`;
|
||||||
p.availability === "available" ? [p] : []
|
|
||||||
).map(p => {
|
|
||||||
const tiers = p.price_tiers.map(t => `${t.min_qty}${t.max_qty === 0 ? "+" : `-${t.max_qty}`}: $${Number(t.price).toFixed(2)}`).join(", ");
|
|
||||||
return `${p.name} (${p.unit_type})\n ${tiers}`;
|
|
||||||
}).join("\n\n")}\n\nGenerated ${generatedDate}. Prices subject to change.`;
|
|
||||||
|
|
||||||
let enqueued = 0;
|
let enqueued = 0;
|
||||||
let failed = 0;
|
let failed = 0;
|
||||||
|
|||||||
@@ -232,9 +232,10 @@ export default function RoadmapPage() {
|
|||||||
className="w-full px-4 py-3 rounded-xl border border-[#e5e5e5] bg-white text-[#1a1a1a] focus:outline-none focus:ring-2 focus:ring-[#1a4d2e]/50 focus:border-[#1a4d2e] transition-all"
|
className="w-full px-4 py-3 rounded-xl border border-[#e5e5e5] bg-white text-[#1a1a1a] focus:outline-none focus:ring-2 focus:ring-[#1a4d2e]/50 focus:border-[#1a4d2e] transition-all"
|
||||||
>
|
>
|
||||||
<option value="">Select a category</option>
|
<option value="">Select a category</option>
|
||||||
{CATEGORIES.flatMap(c =>
|
{CATEGORIES.reduce<typeof CATEGORIES>((acc, c) => {
|
||||||
c === "All" ? [] : [c]
|
if (c !== "All") acc.push(c);
|
||||||
).map((category) => (
|
return acc;
|
||||||
|
}, []).map((category) => (
|
||||||
<option key={category} value={category.toLowerCase()}>{category}</option>
|
<option key={category} value={category.toLowerCase()}>{category}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -42,15 +42,15 @@ type ShippingFulfillmentPanelProps = {
|
|||||||
canManageOrders: boolean;
|
canManageOrders: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SHIPPING_STATUSES = [
|
const SHIPPING_STATUSES: Array<{ value: ShippingStatus; label: string }> = [
|
||||||
{ value: "pending", label: "Pending" },
|
{ value: "pending", label: "Pending" },
|
||||||
{ value: "label_created", label: "Label Created" },
|
{ value: "label_created", label: "Label Created" },
|
||||||
{ value: "shipped", label: "Shipped" },
|
{ value: "shipped", label: "Shipped" },
|
||||||
{ value: "delivered", label: "Delivered" },
|
{ value: "delivered", label: "Delivered" },
|
||||||
{ value: "returned", label: "Returned" },
|
{ value: "returned", label: "Returned" },
|
||||||
] as const;
|
];
|
||||||
|
|
||||||
type ShippingStatus = (typeof SHIPPING_STATUSES)[number]["value"];
|
type ShippingStatus = "pending" | "label_created" | "shipped" | "delivered" | "returned";
|
||||||
|
|
||||||
const SERVICE_LABELS: Record<FedExServiceType, string> = {
|
const SERVICE_LABELS: Record<FedExServiceType, string> = {
|
||||||
FEDEX_OVERNIGHT: "FedEx Overnight",
|
FEDEX_OVERNIGHT: "FedEx Overnight",
|
||||||
@@ -526,9 +526,10 @@ function OrderCard({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{SHIPPING_STATUSES.flatMap((s) =>
|
{SHIPPING_STATUSES.reduce<typeof SHIPPING_STATUSES>((acc, s) => {
|
||||||
s.value !== order.shipping_status && s.value !== "label_created" ? [s] : []
|
if (s.value !== order.shipping_status && s.value !== "label_created") acc.push(s);
|
||||||
).map((s) => {
|
return acc;
|
||||||
|
}, []).map((s) => {
|
||||||
const isDanger = s.value === "returned";
|
const isDanger = s.value === "returned";
|
||||||
return (
|
return (
|
||||||
<AdminButton
|
<AdminButton
|
||||||
|
|||||||
@@ -758,9 +758,10 @@ export default function LotDetailPanel({
|
|||||||
className="w-full rounded-xl border border-[var(--admin-border)] bg-[var(--admin-bg-subtle)]/50 px-3 py-3 text-sm text-[var(--admin-text-primary)] outline-none focus:border-[var(--admin-accent)] focus:ring-2 focus:ring-[var(--admin-accent)]/20 transition-all"
|
className="w-full rounded-xl border border-[var(--admin-border)] bg-[var(--admin-bg-subtle)]/50 px-3 py-3 text-sm text-[var(--admin-text-primary)] outline-none focus:border-[var(--admin-accent)] focus:ring-2 focus:ring-[var(--admin-accent)]/20 transition-all"
|
||||||
>
|
>
|
||||||
<option value="">Select status...</option>
|
<option value="">Select status...</option>
|
||||||
{STATUS_FLOW.flatMap((s) =>
|
{STATUS_FLOW.reduce<typeof STATUS_FLOW>((acc, s) => {
|
||||||
s !== lot.status ? [s] : []
|
if (s !== lot.status) acc.push(s);
|
||||||
).map((s) => {
|
return acc;
|
||||||
|
}, []).map((s) => {
|
||||||
const c = EVENT_CONFIG[s];
|
const c = EVENT_CONFIG[s];
|
||||||
return (
|
return (
|
||||||
<option key={s} value={s}>
|
<option key={s} value={s}>
|
||||||
|
|||||||
+4
-2
@@ -206,13 +206,15 @@ class MockQueryBuilder<T extends Record<string, unknown> = Record<string, unknow
|
|||||||
return (
|
return (
|
||||||
typeof rowValue === "string" &&
|
typeof rowValue === "string" &&
|
||||||
likeNeedle !== null &&
|
likeNeedle !== null &&
|
||||||
rowValue.includes(likeNeedle)
|
likeNeedle.length > 0 &&
|
||||||
|
rowValue.indexOf(likeNeedle) !== -1
|
||||||
);
|
);
|
||||||
case "ilike":
|
case "ilike":
|
||||||
return (
|
return (
|
||||||
typeof rowValue === "string" &&
|
typeof rowValue === "string" &&
|
||||||
ilikeNeedle !== null &&
|
ilikeNeedle !== null &&
|
||||||
rowValue.toLowerCase().includes(ilikeNeedle)
|
ilikeNeedle.length > 0 &&
|
||||||
|
rowValue.toLowerCase().indexOf(ilikeNeedle) !== -1
|
||||||
);
|
);
|
||||||
case "in":
|
case "in":
|
||||||
return inSet !== null && inSet.has(rowValue);
|
return inSet !== null && inSet.has(rowValue);
|
||||||
|
|||||||
Reference in New Issue
Block a user