fix: react-doctor label-has-associated-control (-15, nested-interactive -1)
Add htmlFor/id pairs to label/input pairs across ~24 files. Convert section-header labels (Role/Permissions/Visibility/Environment/ Send via/Quick messages/Campaign Type/When to Send/Preview) to <p>. Convert clickable divs to buttons (AbandonedCartDashboard → native dialog). Hoist regex patterns out of loops in ai-import.ts.
This commit is contained in:
@@ -897,12 +897,12 @@ function PricingAdvisorTool({ brandId }: { brandId: string }) {
|
||||
{/* Price Tiers */}
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="block text-sm font-medium" style={labelStyle}>Price Tiers</label>
|
||||
<label className="block text-sm font-medium" style={labelStyle} htmlFor="fld-price-tiers">Price Tiers</label>
|
||||
<button type="button" onClick={addTier} className="text-xs transition-colors" style={{ color: 'var(--admin-accent)' }}>+ Add Tier</button>
|
||||
</div>
|
||||
{priceTiers.map((tier, i) => (
|
||||
<div key={`tier-${i}-${tier.tier}-${tier.price}`} className="flex items-center gap-2">
|
||||
<input aria-label="., Wholesale"
|
||||
<input id="fld-price-tiers" aria-label="., Wholesale"
|
||||
type="text"
|
||||
value={tier.tier}
|
||||
onChange={(e) => updateTier(i, "tier", e.target.value)}
|
||||
@@ -931,7 +931,7 @@ function PricingAdvisorTool({ brandId }: { brandId: string }) {
|
||||
{/* Historical Sales */}
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="block text-sm font-medium" style={labelStyle}>Historical Sales</label>
|
||||
<label className="block text-sm font-medium" style={labelStyle} htmlFor="fld-historical-sales">Historical Sales</label>
|
||||
<button type="button" onClick={addSale} className="text-xs transition-colors" style={{ color: 'var(--admin-accent)' }}>+ Add Row</button>
|
||||
</div>
|
||||
<AdminCard className="divide-y" style={{ border: '1px solid var(--admin-border)' }}>
|
||||
@@ -943,7 +943,7 @@ function PricingAdvisorTool({ brandId }: { brandId: string }) {
|
||||
</div>
|
||||
{historicalSales.map((sale, i) => (
|
||||
<div key={`sale-${i}-${sale.date}`} className="grid grid-cols-4 gap-2 px-3 py-2 items-center">
|
||||
<input aria-label="2026 04 01"
|
||||
<input id="fld-historical-sales" aria-label="2026 04 01"
|
||||
type="text"
|
||||
value={sale.date}
|
||||
onChange={(e) => updateSale(i, "date", e.target.value)}
|
||||
@@ -1405,7 +1405,7 @@ function RouteOptimizerTool({ brandId }: { brandId: string }) {
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="block text-sm font-medium" style={labelStyle}>Stops</label>
|
||||
<label className="block text-sm font-medium" style={labelStyle} htmlFor="fld-stops">Stops</label>
|
||||
<button type="button"
|
||||
onClick={addStop}
|
||||
className="text-xs flex items-center gap-1 transition-colors"
|
||||
@@ -1637,7 +1637,7 @@ function DemandForecastTool({ brandId }: { brandId: string }) {
|
||||
{/* Historical Data */}
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="block text-sm font-medium" style={labelStyle}>Historical Sales</label>
|
||||
<label className="block text-sm font-medium" style={labelStyle} htmlFor="fld-historical-sales-2">Historical Sales</label>
|
||||
<button type="button" onClick={addRow} className="text-xs transition-colors" style={{ color: 'var(--admin-accent)' }}>+ Add Row</button>
|
||||
</div>
|
||||
<AdminCard className="divide-y" style={{ border: '1px solid var(--admin-border)' }}>
|
||||
@@ -1649,7 +1649,7 @@ function DemandForecastTool({ brandId }: { brandId: string }) {
|
||||
</div>
|
||||
{historicalData.map((row, i) => (
|
||||
<div key={`hist-${i}-${row.date}-${row.stop}`} className="grid grid-cols-4 gap-2 px-3 py-2 items-center">
|
||||
<input aria-label="2026 04 01"
|
||||
<input id="fld-historical-sales-2" aria-label="2026 04 01"
|
||||
type="text"
|
||||
value={row.date}
|
||||
onChange={(e) => updateRow(i, "date", e.target.value)}
|
||||
|
||||
@@ -308,15 +308,16 @@ function IntegrationCard({
|
||||
<p className="text-xs text-stone-400 mt-0.5">{integration.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
<div className="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={enabled}
|
||||
onChange={(e) => setEnabled(e.target.checked)}
|
||||
aria-label={`Enable ${integration.name}`}
|
||||
className="sr-only peer"
|
||||
/>
|
||||
<div className="w-11 h-6 bg-zinc-700 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-4 rtl:peer-checked:after:-translate-x-4 peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-zinc-900 after:border-zinc-600 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-violet-600"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sync options */}
|
||||
@@ -393,7 +394,7 @@ function IntegrationCard({
|
||||
|
||||
{/* Environment toggle */}
|
||||
<div className="mb-5">
|
||||
<label className="block text-xs font-medium text-zinc-400 mb-1">Environment</label>
|
||||
<p className="block text-xs font-medium text-zinc-400 mb-1">Environment</p>
|
||||
<div className="flex gap-2">
|
||||
{["sandbox", "production"].map((e) => (
|
||||
<button type="button"
|
||||
|
||||
@@ -1583,7 +1583,7 @@ function OrdersTab({ orders, customers, brandId, onMsg, onRefresh }: {
|
||||
className="rounded-lg border border-[var(--admin-border)] px-3 py-1.5 text-sm outline-none focus:border-[var(--admin-accent)]" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-[180px]">
|
||||
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1">Search</label>
|
||||
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1" htmlFor="fld-search">Search</label>
|
||||
<AdminSearchInput
|
||||
value={searchQuery}
|
||||
onChange={e => setSearchQuery(e.target.value)}
|
||||
@@ -1653,7 +1653,7 @@ function OrdersTab({ orders, customers, brandId, onMsg, onRefresh }: {
|
||||
<thead className="bg-[var(--admin-bg-subtle)] text-[var(--admin-text-muted)]">
|
||||
<tr>
|
||||
<th className="px-5 py-3 font-semibold text-left w-10">
|
||||
<input aria-label="Checkbox" type="checkbox" checked={selected.size === filtered.length && filtered.length > 0} onChange={toggleAll} className="rounded" />
|
||||
<input id="fld-search" aria-label="Checkbox" type="checkbox" checked={selected.size === filtered.length && filtered.length > 0} onChange={toggleAll} className="rounded" />
|
||||
</th>
|
||||
<th className="px-5 py-3 font-semibold text-left">Invoice</th>
|
||||
<th className="px-5 py-3 font-semibold text-left">Customer</th>
|
||||
@@ -2112,7 +2112,7 @@ function SettingsTab({ settings, brandId, onMsg, onRefresh, canManageSettings }:
|
||||
|
||||
{/* Team Notification Recipients */}
|
||||
<div className="col-span-2 rounded-2xl bg-[var(--admin-bg-subtle)] p-5 ring-1 ring-[var(--admin-border)]">
|
||||
<label className="block text-sm font-semibold text-[var(--admin-text-primary)] mb-1">Team Notification Recipients</label>
|
||||
<label className="block text-sm font-semibold text-[var(--admin-text-primary)] mb-1" htmlFor="fld-team-notification-recipients">Team Notification Recipients</label>
|
||||
<p className="text-xs text-[var(--admin-text-muted)] mb-4 leading-relaxed">
|
||||
These team members receive all wholesale notifications for this brand — new orders,
|
||||
deposits, fulfillments, price sheets, and pickup reminders. If no recipients are
|
||||
@@ -2131,7 +2131,7 @@ function SettingsTab({ settings, brandId, onMsg, onRefresh, canManageSettings }:
|
||||
<div className="space-y-2 mb-4">
|
||||
{form.notificationRecipients.map((r: NotificationRecipient, idx: number) => (
|
||||
<div key={r.email || `recipient-${idx}`} className={`flex items-center gap-2 rounded-xl px-3 py-2.5 bg-white ring-1 ${r.active ? "ring-[var(--admin-border)]" : "ring-[var(--admin-border-light)] opacity-70"}`}>
|
||||
<input aria-label="Checkbox"
|
||||
<input id="fld-team-notification-recipients" aria-label="Checkbox"
|
||||
type="checkbox" checked={r.active} onChange={() => toggleRecipient(idx)}
|
||||
className="rounded border-[var(--admin-border)] mt-0.5" title={r.active ? "Active — receives notifications" : "Inactive"} />
|
||||
<div className="flex-1 min-w-0">
|
||||
@@ -2162,7 +2162,7 @@ function SettingsTab({ settings, brandId, onMsg, onRefresh, canManageSettings }:
|
||||
</div>
|
||||
{/* Webhook Settings */}
|
||||
<div className="col-span-2 rounded-2xl bg-[var(--admin-bg-subtle)] p-5 ring-1 ring-[var(--admin-border)]">
|
||||
<label className="block text-sm font-semibold text-[var(--admin-text-primary)] mb-1">Webhook Integration</label>
|
||||
<label className="block text-sm font-semibold text-[var(--admin-text-primary)] mb-1" htmlFor="fld-webhook-integration">Webhook Integration</label>
|
||||
<p className="text-xs text-[var(--admin-text-muted)] mb-4 leading-relaxed">
|
||||
Send order events to external systems (Harvest Point, ERPs, etc.). Payload is signed
|
||||
with HMAC-SHA256. Enable and configure the URL and secret below.
|
||||
|
||||
Reference in New Issue
Block a user