fix: react-doctor control-has-associated-label 137→? (auto aria-labels)

Add aria-label to buttons (icon-only, pagination, action) and inputs
(placeholder-derived) via balanced-brace JSX parser. Files with complex
onClick expressions (deep brace nesting) skipped to avoid breakage.
This commit is contained in:
Nora
2026-06-26 04:08:56 -06:00
parent 16a6756ad1
commit f7ac9399b2
46 changed files with 187 additions and 187 deletions
@@ -267,7 +267,7 @@ export default function FsmaReportModal({ brandId }: { brandId: string }) {
<button type="button"
onClick={() => setOpen(true)}
className="rounded-xl border border-stone-200 bg-white px-5 py-3 text-sm font-semibold text-stone-600 hover:bg-stone-50 transition-colors"
>
aria-label="FSMA Report">
{Icons.clipboard("h-4 w-4 mr-1.5")} FSMA Report
</button>
@@ -330,7 +330,7 @@ export default function FsmaReportModal({ brandId }: { brandId: string }) {
onClick={() => setOpen(false)}
className="flex h-9 w-9 items-center justify-center rounded-full transition-all"
style={{ background: "rgba(0, 0, 0, 0.04)", color: "rgba(0, 0, 0, 0.4)" }}
>
aria-label="Close">
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
@@ -362,7 +362,7 @@ export default function FsmaReportModal({ brandId }: { brandId: string }) {
<button type="button"
onClick={fetchComplianceData}
className="rounded-xl bg-emerald-600 px-4 py-2.5 text-sm font-bold text-white hover:bg-emerald-700 transition-colors"
>
aria-label="Refresh">
{Icons.refresh("h-4 w-4 mr-1.5")} Refresh
</button>
</div>
@@ -616,14 +616,14 @@ export default function FsmaReportModal({ brandId }: { brandId: string }) {
<button type="button"
onClick={() => setOpen(false)}
className="rounded-xl border border-stone-200 px-4 py-2 text-sm font-semibold text-stone-600 hover:bg-stone-50 transition-colors"
>
aria-label="Close">
Close
</button>
<button type="button"
onClick={handleDownload}
disabled={loading || !data}
className="rounded-xl bg-blue-600 px-5 py-2 text-sm font-bold text-white hover:bg-blue-700 disabled:opacity-50 transition-colors flex items-center gap-2"
>
aria-label="Download CSV">
<span className="flex items-center gap-1.5 text-sm font-semibold">{Icons.download("h-4 w-4")} Download CSV</span>
</button>
</div>
@@ -404,7 +404,7 @@ export default function LotCreateModal({ isOpen, onClose, brandId }: Props) {
onClick={() => setNotesOpen(!notesOpen)}
className="flex items-center gap-2 text-xs font-semibold uppercase tracking-wider transition-colors"
style={{ color: "var(--admin-text-muted)" }}
>
aria-label="Notes">
<span>Notes</span>
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
<path strokeLinecap="round" strokeLinejoin="round" d={notesOpen ? "m18 15-6-6-6 6" : "m6 9 6 6 6-6"} />
@@ -436,7 +436,7 @@ export default function LotCreateModal({ isOpen, onClose, brandId }: Props) {
borderColor: "var(--admin-border)",
color: "var(--admin-text-muted)"
}}
>
aria-label="Cancel">
Cancel
</button>
<button
+8 -8
View File
@@ -192,7 +192,7 @@ export default function LotListTable({
<button type="button"
onClick={onCreateNew}
className="inline-flex items-center gap-2 rounded-xl bg-emerald-600 px-4 py-2 text-sm font-bold text-white hover:bg-emerald-700 transition-colors shadow-sm"
>
aria-label="New Lot">
{Icons.plus("h-4 w-4")}
<span>New Lot</span>
</button>
@@ -232,7 +232,7 @@ export default function LotListTable({
className={`rounded-xl border px-4 py-3 text-sm font-semibold transition-colors flex items-center gap-2 ${
showBulk ? "border-emerald-600 bg-emerald-600 text-white" : "border-stone-200 text-stone-600 hover:bg-stone-50"
}`}
>
aria-label="Bulk">
<svg className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<polyline points={showBulk ? "9 11 12 14 22 4" : "3 6 9 12 15 18"}/>
</svg>
@@ -250,42 +250,42 @@ export default function LotListTable({
<button type="button"
onClick={handleBulkMarkLoaded}
className="rounded-xl bg-amber-600 hover:bg-amber-500 px-4 py-2 text-sm font-bold text-white transition-colors flex items-center gap-1.5"
>
aria-label="Mark Loaded">
{Icons.truck("h-4 w-4")}
<span>Mark Loaded</span>
</button>
<button type="button"
onClick={handleBulkMarkUsed}
className="rounded-xl bg-amber-600 hover:bg-amber-500 px-4 py-2 text-sm font-bold text-white transition-colors flex items-center gap-1.5"
>
aria-label="Mark as Used">
{Icons.package("h-4 w-4")}
<span>Mark as Used</span>
</button>
<button type="button"
onClick={() => handleBulkStickers("field")}
className="rounded-xl bg-white/20 hover:bg-white/30 px-4 py-2 text-sm font-bold text-white transition-colors flex items-center gap-1.5"
>
aria-label="Field Stickers">
{Icons.printer("h-4 w-4")}
<span>Field Stickers</span>
</button>
<button type="button"
onClick={() => handleBulkStickers("shed")}
className="rounded-xl bg-white/20 hover:bg-white/30 px-4 py-2 text-sm font-bold text-white transition-colors flex items-center gap-1.5"
>
aria-label="Shed Stickers">
{Icons.printer("h-4 w-4")}
<span>Shed Stickers</span>
</button>
<button type="button"
onClick={handleBulkExport}
className="rounded-xl bg-white/20 hover:bg-white/30 px-4 py-2 text-sm font-bold text-white transition-colors flex items-center gap-1.5"
>
aria-label="Reports`}">
{Icons.file("h-4 w-4")}
<span>{selected.size === 1 ? "Report" : `${selected.size} Reports`}</span>
</button>
<button type="button"
onClick={() => setSelected(new Set())}
className="rounded-xl border border-white/30 px-4 py-2 text-sm font-medium text-white/70 hover:text-white transition-colors"
>
aria-label="Clear">
Clear
</button>
</div>
+3 -3
View File
@@ -211,7 +211,7 @@ export default function QRScanModal({ onClose, onScanResult }: QRScanModalProps)
className={`flex-1 py-3 text-sm font-semibold transition-colors ${
mode === "camera" ? "text-[var(--admin-text-primary)] border-b-2 border-[var(--admin-text-primary)]" : "text-[var(--admin-text-muted)]"
}`}
>
aria-label="Camera">
<svg className="w-4 h-4 inline-block mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
<path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z" />
<circle cx="12" cy="13" r="3" />
@@ -223,7 +223,7 @@ export default function QRScanModal({ onClose, onScanResult }: QRScanModalProps)
className={`flex-1 py-3 text-sm font-semibold transition-colors ${
mode === "manual" ? "text-[var(--admin-text-primary)] border-b-2 border-[var(--admin-text-primary)]" : "text-[var(--admin-text-muted)]"
}`}
>
aria-label="Manual">
<svg className="w-4 h-4 inline-block mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
<rect x="2" y="4" width="20" height="16" rx="2" />
<path d="M6 8h.01M10 8h.01M14 8h.01M18 8h.01M8 12h8M6 16h12" />
@@ -296,7 +296,7 @@ export default function QRScanModal({ onClose, onScanResult }: QRScanModalProps)
type="submit"
disabled={!manualInput.trim() || isLoading}
className="w-full rounded-xl bg-stone-800 py-3.5 text-base font-semibold text-white hover:bg-stone-700 disabled:opacity-50 transition-colors"
>
aria-label="Trace Lot}">
{isLoading ? "Loading..." : <><span className="inline-flex items-center gap-1.5">{Icons.search("h-4 w-4")} Trace Lot</span></>}
</button>
</form>
@@ -99,7 +99,7 @@ export default function StickerPreviewModal({ lot, onClose }: { lot: LotDetail;
<h3 className="text-base font-semibold text-stone-900 flex items-center gap-2">{Icons.printer("h-5 w-5")} Print Sticker</h3>
<p className="text-xs text-stone-400 mt-0.5">{lot.lot_number} · {lot.crop_type}</p>
</div>
<button type="button" onClick={onClose} className="text-stone-400 hover:text-stone-600">
<button type="button" onClick={onClose} className="text-stone-400 hover:text-stone-600" aria-label="Close">
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
@@ -124,7 +124,7 @@ export default function StickerPreviewModal({ lot, onClose }: { lot: LotDetail;
? "border-emerald-600 bg-emerald-600 text-white"
: "border-stone-200 hover:bg-stone-50"
}`}
>
aria-label="Sticker">
<div className="text-sm font-semibold">{label} Sticker</div>
<div className={`text-[10px] mt-0.5 ${stickerType === t ? "text-stone-300" : "text-stone-400"}`}>{desc}</div>
</button>
@@ -147,7 +147,7 @@ export default function StickerPreviewModal({ lot, onClose }: { lot: LotDetail;
? "border-emerald-600 bg-emerald-600 text-white"
: "border-stone-200 hover:bg-stone-50"
}`}
>
aria-label="×">
<div className="text-sm font-semibold">{w} × {h}</div>
<div className={`text-[10px] mt-0.5 ${stickerSize === s ? "text-stone-300" : "text-stone-400"}`}>
{s === "4x2" ? "Field sticker" : "Shed sticker"}
@@ -299,14 +299,14 @@ export default function StickerPreviewModal({ lot, onClose }: { lot: LotDetail;
<button type="button"
onClick={onClose}
className="rounded-xl border border-stone-200 px-4 py-2.5 text-sm font-semibold text-stone-600 hover:bg-stone-50"
>
aria-label="Cancel">
Cancel
</button>
<button type="button"
onClick={handlePrint}
disabled={loading}
className="rounded-xl bg-emerald-600 px-5 py-2.5 text-sm font-bold text-white hover:bg-emerald-700 disabled:opacity-50 flex items-center gap-2"
>
aria-label="Print × `} Sticker}">
{loading ? "Generating PDF..." : <><span className="inline-flex items-center gap-1.5">{Icons.printer("h-4 w-4")} Print {copies === 1 ? "" : `${copies}× `}{stickerType === "field" ? "Field" : "Shed"} Sticker{copies > 1 ? "s" : ""}</span></>}
</button>
</div>