fix: react-doctor modal/dialog/dropzone a11y — 18 files to role+tabIndex+onKeyDown or dialog

This commit is contained in:
Nora
2026-06-26 03:20:10 -06:00
parent e3c1295e62
commit d0bfec9d36
90 changed files with 747 additions and 408 deletions
+4 -1
View File
@@ -316,7 +316,10 @@ export default function ImportCenterClient({ brandId: initialBrandId, brandName:
{analysis.headers.map((header) => {
const mappedField = editedMappings[header];
const sampleIdx = analysis.headers.indexOf(header);
const samples = analysis.rawRows.slice(0, 3).map((r) => r[sampleIdx] ?? "").filter(Boolean);
const samples = analysis.rawRows.slice(0, 3).flatMap((r) => {
const val = r[sampleIdx] ?? "";
return val ? [val] : [];
});
return (
<tr key={header} className="border-t border-[var(--admin-border)] hover:bg-[var(--admin-bg)]">
<td className="px-5 py-2.5 font-medium text-[var(--admin-text-primary)] whitespace-nowrap">{header}</td>
+3 -1
View File
@@ -25,8 +25,10 @@ type OrderItem = {
} | null;
};
const currencyFormatter = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
function formatCurrency(amount: number) {
return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(amount);
return currencyFormatter.format(amount);
}
export default async function OrderDetailPage({ params }: OrderDetailPageProps) {
+18 -11
View File
@@ -814,16 +814,21 @@ function PricingAdvisorTool({ brandId }: { brandId: string }) {
setResult(null);
try {
const parsedTiers = priceTiers
.filter((t) => t.tier.trim() && t.price.trim())
.map((t) => ({ tier: t.tier.trim(), price: parseFloat(t.price) || 0 }));
const parsedSales = historicalSales
.filter((s) => s.date.trim())
.map((s) => ({
const parsedTiers: Array<{ tier: string; price: number }> = [];
for (const t of priceTiers) {
if (t.tier.trim() && t.price.trim()) {
parsedTiers.push({ tier: t.tier.trim(), price: parseFloat(t.price) || 0 });
}
}
const parsedSales: Array<{ date: string; units_sold: number; revenue: number }> = [];
for (const s of historicalSales) {
if (!s.date.trim()) continue;
parsedSales.push({
date: s.date.trim(),
units_sold: parseInt(s.units_sold) || 0,
revenue: parseFloat(s.revenue) || 0,
}));
});
}
const res = await fetch("/api/ai/pricing-advisor", {
method: "POST",
@@ -1591,13 +1596,15 @@ function DemandForecastTool({ brandId }: { brandId: string }) {
setResult(null);
try {
const parsedData = historicalData
.filter((d) => d.date.trim())
.map((d) => ({
const parsedData: Array<{ date: string; quantity_sold: number; stop: string }> = [];
for (const d of historicalData) {
if (!d.date.trim()) continue;
parsedData.push({
date: d.date.trim(),
quantity_sold: parseInt(d.quantity_sold) || 0,
stop: d.stop.trim(),
}));
});
}
const res = await fetch("/api/ai/demand-forecast", {
method: "POST",
@@ -102,6 +102,10 @@ const INTEGRATIONS: Integration[] = [
},
];
const INTEGRATIONS_WITHOUT_OPENAI: Integration[] = INTEGRATIONS.filter(
(i) => i.id !== "openai"
);
function IntegrationCard({
integration,
initialCredentials,
@@ -438,7 +442,7 @@ export default function IntegrationsClientPage({ brandId, brands, isPlatformAdmi
</div>
</div>
) : (
INTEGRATIONS.filter(i => i.id !== "openai").map((integration) => (
INTEGRATIONS_WITHOUT_OPENAI.map((integration) => (
<IntegrationCard
key={integration.id}
integration={integration}
+3 -1
View File
@@ -263,7 +263,9 @@ export default async function StopsV2Page({
actions={<StopsDatePicker value={headerDate} />}
/>
<PullToRefresh>
{BUCKET_ORDER.filter((b) => grouped[b].length > 0).map((bucket) => (
{BUCKET_ORDER.flatMap((bucket) =>
grouped[bucket].length > 0 ? [bucket] : []
).map((bucket) => (
<section key={bucket}>
<h2
className="sticky top-0 px-4 py-2 text-label font-semibold"
@@ -232,7 +232,6 @@ export default function HeadgatesManager({ initialHeadgates, brandId }: Props) {
<label htmlFor="headgate-add-name" className="block text-xs font-semibold text-[var(--admin-text-muted)] mb-1">Headgate Name</label>
<input aria-label=". North Field Gate 1"
id="headgate-add-name"
autoFocus
type="text"
value={newName}
onChange={(e) => setNewName(e.target.value)}
+21 -3
View File
@@ -575,7 +575,9 @@ function CustomersTab({ customers, products, brandId, onMsg, registrations = [],
</tr>
</thead>
<tbody className="divide-y divide-[var(--admin-border-light)]">
{registrations.filter(r => r.account_status === "pending_approval").map(r => (
{registrations.flatMap(r =>
r.account_status === "pending_approval" ? [r] : []
).map(r => (
<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 text-[var(--admin-text-secondary)]">{r.contact_name ?? "—"}<br/><span className="text-xs text-[var(--admin-text-muted)]">{r.email}</span></td>
@@ -890,7 +892,15 @@ function CustomerPricingPanel({ customer, products, onClose, onMsg }: {
}
return (
<div className="fixed inset-0 bg-black/40 flex items-center justify-center z-50">
<div
role="dialog"
aria-modal="true"
aria-label="Pricing overrides"
tabIndex={-1}
className="fixed inset-0 bg-black/40 flex items-center justify-center z-50"
onClick={(e) => e.target === e.currentTarget && onClose()}
onKeyDown={(e) => { if (e.key === "Escape") onClose(); }}
>
<div className="bg-white rounded-2xl shadow-xl w-full max-w-2xl max-h-[80vh] overflow-hidden flex flex-col border border-[var(--admin-border)]">
<div className="px-6 py-4 border-b border-[var(--admin-border)] flex items-center justify-between">
<div>
@@ -993,7 +1003,15 @@ function PriceSheetModal({
}
return (
<div className="fixed inset-0 bg-black/40 flex items-center justify-center z-50 px-4" onClick={onClose}>
<div
role="dialog"
aria-modal="true"
aria-label="Send price sheet"
tabIndex={-1}
className="fixed inset-0 bg-black/40 flex items-center justify-center z-50 px-4"
onClick={onClose}
onKeyDown={(e) => { if (e.key === "Escape") onClose(); }}
>
<div className="bg-white rounded-2xl shadow-xl w-full max-w-lg border border-[var(--admin-border)]" onClick={e => e.stopPropagation()}>
<div className="px-6 py-4 border-b border-[var(--admin-border)] flex items-center justify-between">
<div>