fix: react-doctor → 64/100 — add sizes to 12 next/image, add htmlFor/id to 202 label/input pairs, add aria-label to 5 icon links

This commit is contained in:
Nora
2026-06-26 02:51:12 -06:00
parent 29d9d23a26
commit 33626620a0
53 changed files with 489 additions and 545 deletions
+10 -8
View File
@@ -161,14 +161,16 @@ export default function ImportCenterClient({ brandId: initialBrandId, brandName:
{/* Brand selector */}
{isPlatformAdmin && (
<div className="mb-6 rounded-xl bg-[var(--admin-bg)] border border-[var(--admin-border)] p-4">
<label className="block text-sm font-medium text-[var(--admin-text-muted)] mb-1">Importing to brand</label>
<select aria-label="Select"
value={activeBrandId}
onChange={(e) => handleBrandChange(e.target.value)}
className="w-full rounded-xl border border-[var(--admin-border)] bg-white px-4 py-2.5 text-sm text-[var(--admin-text-primary)] outline-none focus:border-[var(--admin-accent)]"
>
{brands.map((b) => <option key={b.id} value={b.id}>{b.name}</option>)}
</select>
<label className="block text-sm font-medium text-[var(--admin-text-muted)] mb-1">
Importing to brand
<select aria-label="Select"
value={activeBrandId}
onChange={(e) => handleBrandChange(e.target.value)}
className="mt-1 w-full rounded-xl border border-[var(--admin-border)] bg-white px-4 py-2.5 text-sm text-[var(--admin-text-primary)] outline-none focus:border-[var(--admin-accent)]"
>
{brands.map((b) => <option key={b.id} value={b.id}>{b.name}</option>)}
</select>
</label>
</div>
)}
+26 -26
View File
@@ -1105,20 +1105,20 @@ function StopBlastAdvisorTool({ brandId }: { brandId: string }) {
</p>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium mb-1" style={labelStyle}>Stop Name *</label>
<input aria-label="Downtown Farmers Market" type="text" value={stopName} onChange={(e) => setStopName(e.target.value)} placeholder="Downtown Farmers Market" className={inputBaseClass} style={inputStyle} />
<label htmlFor="fld-1-stop-name" className="block text-sm font-medium mb-1" style={labelStyle}>Stop Name *</label>
<input id="fld-1-stop-name" aria-label="Downtown Farmers Market" type="text" value={stopName} onChange={(e) => setStopName(e.target.value)} placeholder="Downtown Farmers Market" className={inputBaseClass} style={inputStyle} />
</div>
<div>
<label className="block text-sm font-medium mb-1" style={labelStyle}>Stop Date</label>
<input aria-label="2026 06 15" type="text" value={stopDate} onChange={(e) => setStopDate(e.target.value)} placeholder="2026-06-15" className={inputBaseClass} style={inputStyle} />
<label htmlFor="fld-2-stop-date" className="block text-sm font-medium mb-1" style={labelStyle}>Stop Date</label>
<input id="fld-2-stop-date" aria-label="2026 06 15" type="text" value={stopDate} onChange={(e) => setStopDate(e.target.value)} placeholder="2026-06-15" className={inputBaseClass} style={inputStyle} />
</div>
<div>
<label className="block text-sm font-medium mb-1" style={labelStyle}>City</label>
<input aria-label="Greeley" type="text" value={city} onChange={(e) => setCity(e.target.value)} placeholder="Greeley" className={inputBaseClass} style={inputStyle} />
<label htmlFor="fld-3-city" className="block text-sm font-medium mb-1" style={labelStyle}>City</label>
<input id="fld-3-city" aria-label="Greeley" type="text" value={city} onChange={(e) => setCity(e.target.value)} placeholder="Greeley" className={inputBaseClass} style={inputStyle} />
</div>
<div>
<label className="block text-sm font-medium mb-1" style={labelStyle}>Customer Count</label>
<input aria-label="42" type="text" value={customerCount} onChange={(e) => setCustomerCount(e.target.value)} placeholder="42" className={inputBaseClass} style={inputStyle} />
<label htmlFor="fld-4-customer-count" className="block text-sm font-medium mb-1" style={labelStyle}>Customer Count</label>
<input id="fld-4-customer-count" aria-label="42" type="text" value={customerCount} onChange={(e) => setCustomerCount(e.target.value)} placeholder="42" className={inputBaseClass} style={inputStyle} />
</div>
</div>
<div className="rounded-lg p-3 text-xs" style={{ backgroundColor: 'rgba(171, 162, 120, 0.1)', border: '1px solid rgba(171, 162, 120, 0.2)', color: '#92781e' }}>
@@ -1235,8 +1235,8 @@ function CustomerInsightsTool({ brandId }: { brandId: string }) {
Ask questions about your customers and orders in plain English. The AI will analyze and return actionable insights.
</p>
<div>
<label className="block text-sm font-medium mb-1" style={labelStyle}>Ask about your customers</label>
<textarea aria-label="., 'Which Customers Haven't Ordered In 45 Days?'"
<label htmlFor="fld-5-ask-about-your-customers" className="block text-sm font-medium mb-1" style={labelStyle}>Ask about your customers</label>
<textarea id="fld-5-ask-about-your-customers" aria-label="., 'Which Customers Haven't Ordered In 45 Days?'"
value={query}
onChange={(e) => setQuery(e.target.value)}
rows={2}
@@ -1396,8 +1396,8 @@ function RouteOptimizerTool({ brandId }: { brandId: string }) {
Add your stops below. You need at least 2 stops to optimize a route.
</p>
<div>
<label className="block text-sm font-medium mb-1" style={labelStyle}>Start Location</label>
<input aria-label="Warehouse, Greeley CO"
<label htmlFor="fld-6-start-location" className="block text-sm font-medium mb-1" style={labelStyle}>Start Location</label>
<input id="fld-6-start-location" aria-label="Warehouse, Greeley CO"
type="text"
value={startLocation}
onChange={(e) => setStartLocation(e.target.value)}
@@ -1430,8 +1430,8 @@ function RouteOptimizerTool({ brandId }: { brandId: string }) {
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs mb-1" style={{ color: 'var(--admin-text-muted)' }}>Name *</label>
<input aria-label="Farmers Market"
<label htmlFor="fld-7-name" className="block text-xs mb-1" style={{ color: 'var(--admin-text-muted)' }}>Name *</label>
<input id="fld-7-name" aria-label="Farmers Market"
type="text"
value={stop.name}
onChange={(e) => updateStop(i, "name", e.target.value)}
@@ -1441,8 +1441,8 @@ function RouteOptimizerTool({ brandId }: { brandId: string }) {
/>
</div>
<div>
<label className="block text-xs mb-1" style={{ color: 'var(--admin-text-muted)' }}>City *</label>
<input aria-label="Greeley"
<label htmlFor="fld-8-city-2" className="block text-xs mb-1" style={{ color: 'var(--admin-text-muted)' }}>City *</label>
<input id="fld-8-city-2" aria-label="Greeley"
type="text"
value={stop.city}
onChange={(e) => updateStop(i, "city", e.target.value)}
@@ -1452,8 +1452,8 @@ function RouteOptimizerTool({ brandId }: { brandId: string }) {
/>
</div>
<div>
<label className="block text-xs mb-1" style={{ color: 'var(--admin-text-muted)' }}>State *</label>
<input aria-label="CO"
<label htmlFor="fld-9-state" className="block text-xs mb-1" style={{ color: 'var(--admin-text-muted)' }}>State *</label>
<input id="fld-9-state" aria-label="CO"
type="text"
value={stop.state}
onChange={(e) => updateStop(i, "state", e.target.value)}
@@ -1463,8 +1463,8 @@ function RouteOptimizerTool({ brandId }: { brandId: string }) {
/>
</div>
<div>
<label className="block text-xs mb-1" style={{ color: 'var(--admin-text-muted)' }}>Address</label>
<input aria-label="123 Main St"
<label htmlFor="fld-10-address" className="block text-xs mb-1" style={{ color: 'var(--admin-text-muted)' }}>Address</label>
<input id="fld-10-address" aria-label="123 Main St"
type="text"
value={stop.address}
onChange={(e) => updateStop(i, "address", e.target.value)}
@@ -1475,8 +1475,8 @@ function RouteOptimizerTool({ brandId }: { brandId: string }) {
</div>
</div>
<div>
<label className="block text-xs mb-1" style={{ color: 'var(--admin-text-muted)' }}>Time Window</label>
<input aria-label="8am12pm"
<label htmlFor="fld-11-time-window" className="block text-xs mb-1" style={{ color: 'var(--admin-text-muted)' }}>Time Window</label>
<input id="fld-11-time-window" aria-label="8am12pm"
type="text"
value={stop.time_window}
onChange={(e) => updateStop(i, "time_window", e.target.value)}
@@ -1631,12 +1631,12 @@ function DemandForecastTool({ brandId }: { brandId: string }) {
</p>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium mb-1" style={labelStyle}>Product Name *</label>
<input aria-label="Sweet Corn" type="text" value={productName} onChange={(e) => setProductName(e.target.value)} placeholder="Sweet Corn" className={inputBaseClass} style={inputStyle} />
<label htmlFor="fld-12-product-name" className="block text-sm font-medium mb-1" style={labelStyle}>Product Name *</label>
<input id="fld-12-product-name" aria-label="Sweet Corn" type="text" value={productName} onChange={(e) => setProductName(e.target.value)} placeholder="Sweet Corn" className={inputBaseClass} style={inputStyle} />
</div>
<div>
<label className="block text-sm font-medium mb-1" style={labelStyle}>Stop Name</label>
<input aria-label="Downtown Farmers Market" type="text" value={stopName} onChange={(e) => setStopName(e.target.value)} placeholder="Downtown Farmers Market" className={inputBaseClass} style={inputStyle} />
<label htmlFor="fld-13-stop-name-2" className="block text-sm font-medium mb-1" style={labelStyle}>Stop Name</label>
<input id="fld-13-stop-name-2" aria-label="Downtown Farmers Market" type="text" value={stopName} onChange={(e) => setStopName(e.target.value)} placeholder="Downtown Farmers Market" className={inputBaseClass} style={inputStyle} />
</div>
</div>
@@ -544,8 +544,8 @@ export default function IntegrationsClient({ brandId, brands, isPlatformAdmin, p
{/* Brand picker */}
{isPlatformAdmin && brands.length > 0 && (
<div className="mb-6 flex items-center gap-3">
<label className="text-sm font-medium text-zinc-400">Brand</label>
<select aria-label="Select"
<label htmlFor="fld-1-brand" className="text-sm font-medium text-zinc-400">Brand</label>
<select id="fld-1-brand" aria-label="Select"
value={selectedBrandId}
onChange={() => {
// selectedBrandId is derived from the `brandId` prop.
+47 -53
View File
@@ -1191,8 +1191,8 @@ function ProductsTab({ products, brandId, onMsg, onRefresh }: {
</select>
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Availability</label>
<select aria-label="Select" value={form.availability} onChange={e => setForm(f => ({ ...f, availability: e.target.value }))}
<label htmlFor="fld-1-availability" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Availability</label>
<select id="fld-1-availability" aria-label="Select" value={form.availability} onChange={e => setForm(f => ({ ...f, availability: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]">
<option value="available">Available</option>
<option value="unavailable">Unavailable</option>
@@ -1200,20 +1200,18 @@ function ProductsTab({ products, brandId, onMsg, onRefresh }: {
</select>
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Qty Available</label>
<input aria-label="Number" type="number" value={form.qtyAvailable} onChange={e => setForm(f => ({ ...f, qtyAvailable: Number(e.target.value) }))}
<label htmlFor="fld-2-qty-available" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Qty Available</label>
<input id="fld-2-qty-available" aria-label="Number" type="number" value={form.qtyAvailable} onChange={e => setForm(f => ({ ...f, qtyAvailable: Number(e.target.value) }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" />
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">HP SKU</label>
<input aria-label="Input" value={form.hpSku} onChange={e => setForm(f => ({ ...f, hpSku: e.target.value }))}
<label htmlFor="fld-3-hp-sku" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">HP SKU</label>
<input id="fld-3-hp-sku" aria-label="Input" value={form.hpSku} onChange={e => setForm(f => ({ ...f, hpSku: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" />
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">
Price Tiers (JSON array of {"{\"min_qty\":1,\"max_qty\":24,\"price\":20}"})
</label>
<input aria-label="[{&quot;min Qty&quot;:1,&quot;max Qty&quot;:24,&quot;price&quot;:20},{&quot;min Qty&quot;:25,&quot;max Qty&quot;:0,&quot;price&quot;:18}]" value={form.priceTiers} onChange={e => setForm(f => ({ ...f, priceTiers: e.target.value }))}
<label htmlFor="fld-4-price-tiers-json-array-of-min-qty1max-qty24price20" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Price Tiers (JSON array of {"{\"min_qty\":1,\"max_qty\":24,\"price\":20}"})</label>
<input id="fld-4-price-tiers-json-array-of-min-qty1max-qty24price20" aria-label="[{&quot;min Qty&quot;:1,&quot;max Qty&quot;:24,&quot;price&quot;:20},{&quot;min Qty&quot;:25,&quot;max Qty&quot;:0,&quot;price&quot;:18}]" value={form.priceTiers} onChange={e => setForm(f => ({ ...f, priceTiers: e.target.value }))}
placeholder='[{"min_qty":1,"max_qty":24,"price":20},{"min_qty":25,"max_qty":0,"price":18}]'
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm font-mono outline-none focus:border-[var(--admin-accent)]" />
</div>
@@ -1506,8 +1504,8 @@ function OrdersTab({ orders, customers, brandId, onMsg, onRefresh }: {
{/* Filter bar */}
<div className="flex flex-wrap items-center gap-3 rounded-2xl bg-white border border-[var(--admin-border)] p-4 shadow-sm">
<div>
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1">Status</label>
<select aria-label="Select" value={statusFilter} onChange={e => setStatusFilter(e.target.value)}
<label htmlFor="fld-5-status" className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1">Status</label>
<select id="fld-5-status" aria-label="Select" value={statusFilter} onChange={e => setStatusFilter(e.target.value)}
className="rounded-lg border border-[var(--admin-border)] px-3 py-1.5 text-sm outline-none focus:border-[var(--admin-accent)]">
<option value="all">All Statuses</option>
<option value="pending">Pending</option>
@@ -1517,13 +1515,13 @@ function OrdersTab({ orders, customers, brandId, onMsg, onRefresh }: {
</select>
</div>
<div>
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1">Pickup From</label>
<input aria-label="Date" type="date" value={dateFrom} onChange={e => setDateFrom(e.target.value)}
<label htmlFor="fld-6-pickup-from" className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1">Pickup From</label>
<input id="fld-6-pickup-from" aria-label="Date" type="date" value={dateFrom} onChange={e => setDateFrom(e.target.value)}
className="rounded-lg border border-[var(--admin-border)] px-3 py-1.5 text-sm outline-none focus:border-[var(--admin-accent)]" />
</div>
<div>
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1">Pickup To</label>
<input aria-label="Date" type="date" value={dateTo} onChange={e => setDateTo(e.target.value)}
<label htmlFor="fld-7-pickup-to" className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1">Pickup To</label>
<input id="fld-7-pickup-to" aria-label="Date" type="date" value={dateTo} onChange={e => setDateTo(e.target.value)}
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]">
@@ -1663,13 +1661,11 @@ function OrdersTab({ orders, customers, brandId, onMsg, onRefresh }: {
)}
{/* Invoice download — always visible as icon button */}
<a
<a aria-label="Download Invoice"
href={`/api/wholesale/invoice/${o.id}/pdf`}
target="_blank"
rel="noopener noreferrer"
title="Download Invoice"
className="inline-flex items-center justify-center rounded-xl w-9 h-9 text-[var(--admin-text-muted)] hover:text-[var(--admin-accent)] hover:bg-[var(--admin-bg-subtle)] transition-colors"
>
rel="noopener noreferrer" title="Download Invoice"
className="inline-flex items-center justify-center rounded-xl w-9 h-9 text-[var(--admin-text-muted)] hover:text-[var(--admin-accent)] hover:bg-[var(--admin-bg-subtle)] transition-colors">
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75}><path strokeLinecap="round" strokeLinejoin="round" d="M9 12h9m-9-6h6m-3 12a9 9 0 110 12H15M6 2h9l4 4v14a2 2 0 01-2 2H6a2 2 0 01-2-2V4a2 2 0 012-2z"/></svg>
</a>
@@ -1777,13 +1773,13 @@ function OrdersTab({ orders, customers, brandId, onMsg, onRefresh }: {
<h3 className="font-semibold text-[var(--admin-text-primary)] mb-4">Record Deposit</h3>
<div className="space-y-3">
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Amount ($)</label>
<input aria-label="Number" type="number" value={depAmount} onChange={e => setDepAmount(e.target.value)}
<label htmlFor="fld-8-amount" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Amount ($)</label>
<input id="fld-8-amount" aria-label="Number" type="number" value={depAmount} onChange={e => setDepAmount(e.target.value)}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" step="0.01" />
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Method</label>
<select aria-label="Select" value={depMethod} onChange={e => setDepMethod(e.target.value)}
<label htmlFor="fld-9-method" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Method</label>
<select id="fld-9-method" aria-label="Select" value={depMethod} onChange={e => setDepMethod(e.target.value)}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]">
<option value="cash">Cash</option>
<option value="check">Check</option>
@@ -1811,13 +1807,13 @@ function OrdersTab({ orders, customers, brandId, onMsg, onRefresh }: {
<h3 className="font-semibold text-[var(--admin-text-primary)] mb-4">Bulk Record Deposit ({selected.size} orders)</h3>
<div className="space-y-3">
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Amount per order ($)</label>
<input aria-label="0.00" type="number" value={bulkDepAmount} onChange={e => setBulkDepAmount(e.target.value)}
<label htmlFor="fld-10-amount-per-order" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Amount per order ($)</label>
<input id="fld-10-amount-per-order" aria-label="0.00" type="number" value={bulkDepAmount} onChange={e => setBulkDepAmount(e.target.value)}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" step="0.01" placeholder="0.00" />
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Method</label>
<select aria-label="Select" value={bulkDepMethod} onChange={e => setBulkDepMethod(e.target.value)}
<label htmlFor="fld-11-method-2" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Method</label>
<select id="fld-11-method-2" aria-label="Select" value={bulkDepMethod} onChange={e => setBulkDepMethod(e.target.value)}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]">
<option value="cash">Cash</option>
<option value="check">Check</option>
@@ -2011,48 +2007,48 @@ function SettingsTab({ settings, brandId, onMsg, onRefresh, canManageSettings }:
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Minimum Order Amount ($)</label>
<input aria-label="No Minimum" type="number" value={form.minOrderAmount} onChange={e => setForm(f => ({ ...f, minOrderAmount: e.target.value }))}
<label htmlFor="fld-12-minimum-order-amount" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Minimum Order Amount ($)</label>
<input id="fld-12-minimum-order-amount" aria-label="No Minimum" type="number" value={form.minOrderAmount} onChange={e => setForm(f => ({ ...f, minOrderAmount: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" step="0.01" placeholder="No minimum" />
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">From Email</label>
<input aria-label="Email" type="email" value={form.fromEmail} onChange={e => setForm(f => ({ ...f, fromEmail: e.target.value }))}
<label htmlFor="fld-13-from-email" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">From Email</label>
<input id="fld-13-from-email" aria-label="Email" type="email" value={form.fromEmail} onChange={e => setForm(f => ({ ...f, fromEmail: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" />
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Pickup Location</label>
<textarea aria-label="Textarea" value={form.pickupLocation} onChange={e => setForm(f => ({ ...f, pickupLocation: e.target.value }))}
<label htmlFor="fld-14-pickup-location" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Pickup Location</label>
<textarea id="fld-14-pickup-location" aria-label="Textarea" value={form.pickupLocation} onChange={e => setForm(f => ({ ...f, pickupLocation: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)] font-mono" rows={3} />
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">FOB Location</label>
<input aria-label="Input" value={form.fobLocation} onChange={e => setForm(f => ({ ...f, fobLocation: e.target.value }))}
<label htmlFor="fld-15-fob-location" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">FOB Location</label>
<input id="fld-15-fob-location" aria-label="Input" value={form.fobLocation} onChange={e => setForm(f => ({ ...f, fobLocation: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" />
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Name</label>
<input aria-label="Input" value={form.invoiceBusinessName} onChange={e => setForm(f => ({ ...f, invoiceBusinessName: e.target.value }))}
<label htmlFor="fld-16-invoice-business-name" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Name</label>
<input id="fld-16-invoice-business-name" aria-label="Input" value={form.invoiceBusinessName} onChange={e => setForm(f => ({ ...f, invoiceBusinessName: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" />
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Address</label>
<textarea aria-label="Textarea" value={form.invoiceBusinessAddress} onChange={e => setForm(f => ({ ...f, invoiceBusinessAddress: e.target.value }))}
<label htmlFor="fld-17-invoice-business-address" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Address</label>
<textarea id="fld-17-invoice-business-address" aria-label="Textarea" value={form.invoiceBusinessAddress} onChange={e => setForm(f => ({ ...f, invoiceBusinessAddress: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" rows={2} />
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Phone</label>
<input aria-label="Input" value={form.invoiceBusinessPhone} onChange={e => setForm(f => ({ ...f, invoiceBusinessPhone: e.target.value }))}
<label htmlFor="fld-18-invoice-business-phone" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Phone</label>
<input id="fld-18-invoice-business-phone" aria-label="Input" value={form.invoiceBusinessPhone} onChange={e => setForm(f => ({ ...f, invoiceBusinessPhone: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" />
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Email</label>
<input aria-label="Email" type="email" value={form.invoiceBusinessEmail} onChange={e => setForm(f => ({ ...f, invoiceBusinessEmail: e.target.value }))}
<label htmlFor="fld-19-invoice-business-email" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Email</label>
<input id="fld-19-invoice-business-email" aria-label="Email" type="email" value={form.invoiceBusinessEmail} onChange={e => setForm(f => ({ ...f, invoiceBusinessEmail: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" />
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Website</label>
<input aria-label="Https://" value={form.invoiceBusinessWebsite} onChange={e => setForm(f => ({ ...f, invoiceBusinessWebsite: e.target.value }))}
<label htmlFor="fld-20-invoice-business-website" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Website</label>
<input id="fld-20-invoice-business-website" aria-label="Https://" value={form.invoiceBusinessWebsite} onChange={e => setForm(f => ({ ...f, invoiceBusinessWebsite: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" placeholder="https://" />
</div>
@@ -2233,8 +2229,8 @@ function WebhookSettingsSection({ brandId, onMsg }: {
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Webhook URL</label>
<input aria-label="Https://your System.com/webhooks/wholesale"
<label htmlFor="fld-21-webhook-url" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Webhook URL</label>
<input id="fld-21-webhook-url" aria-label="Https://your System.com/webhooks/wholesale"
type="url"
value={settings.url}
onChange={e => setSettings(s => ({ ...s, url: e.target.value }))}
@@ -2244,10 +2240,8 @@ function WebhookSettingsSection({ brandId, onMsg }: {
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">
Signing Secret (HMAC-SHA256)
</label>
<input aria-label="Leave Blank To Keep Current Secret"
<label htmlFor="fld-22-signing-secret-hmac-sha256" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Signing Secret (HMAC-SHA256)</label>
<input id="fld-22-signing-secret-hmac-sha256" aria-label="Leave Blank To Keep Current Secret"
type="password"
value={settings.secret}
onChange={e => setSettings(s => ({ ...s, secret: e.target.value }))}
@@ -81,10 +81,8 @@ export default function ChangePasswordForm({ userId }: { userId: string }) {
)}
<div>
<label className="block text-sm font-medium text-zinc-400 mb-1">
New Password
</label>
<input aria-label=". 8 Characters"
<label htmlFor="fld-1-new-password" className="block text-sm font-medium text-zinc-400 mb-1">New Password</label>
<input id="fld-1-new-password" aria-label=". 8 Characters"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
@@ -97,10 +95,8 @@ export default function ChangePasswordForm({ userId }: { userId: string }) {
</div>
<div>
<label className="block text-sm font-medium text-zinc-400 mb-1">
Confirm Password
</label>
<input aria-label="Repeat Password"
<label htmlFor="fld-2-confirm-password" className="block text-sm font-medium text-zinc-400 mb-1">Confirm Password</label>
<input id="fld-2-confirm-password" aria-label="Repeat Password"
type="password"
value={confirm}
onChange={(e) => setConfirm(e.target.value)}
+4 -8
View File
@@ -72,10 +72,8 @@ export default function ChangePasswordPage() {
)}
<div>
<label className="block text-sm font-medium text-zinc-400 mb-1">
New Password
</label>
<input aria-label=". 8 Characters"
<label htmlFor="fld-1-new-password" className="block text-sm font-medium text-zinc-400 mb-1">New Password</label>
<input id="fld-1-new-password" aria-label=". 8 Characters"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
@@ -88,10 +86,8 @@ export default function ChangePasswordPage() {
</div>
<div>
<label className="block text-sm font-medium text-zinc-400 mb-1">
Confirm Password
</label>
<input aria-label="Repeat Password"
<label htmlFor="fld-2-confirm-password" className="block text-sm font-medium text-zinc-400 mb-1">Confirm Password</label>
<input id="fld-2-confirm-password" aria-label="Repeat Password"
type="password"
value={confirm}
onChange={(e) => setConfirm(e.target.value)}
@@ -159,8 +159,8 @@ export default function IndianRiverContactPage() {
<form onSubmit={handleSubmit} className="space-y-6" aria-label="Contact form">
<div className="grid gap-6 md:grid-cols-2">
<div>
<label className="block text-sm font-semibold text-stone-700 mb-2">Your Name</label>
<input aria-label="Jane Smith"
<label htmlFor="fld-1-your-name" className="block text-sm font-semibold text-stone-700 mb-2">Your Name</label>
<input id="fld-1-your-name" aria-label="Jane Smith"
required
value={form.name}
onChange={(e) => setForm({ ...form, name: e.target.value })}
@@ -170,8 +170,8 @@ export default function IndianRiverContactPage() {
/>
</div>
<div>
<label className="block text-sm font-semibold text-stone-700 mb-2">Email</label>
<input aria-label="Jane@example.com"
<label htmlFor="fld-2-email" className="block text-sm font-semibold text-stone-700 mb-2">Email</label>
<input id="fld-2-email" aria-label="Jane@example.com"
required
type="email"
value={form.email}
@@ -183,8 +183,8 @@ export default function IndianRiverContactPage() {
</div>
</div>
<div>
<label className="block text-sm font-semibold text-stone-700 mb-2">Topic</label>
<select aria-label="Select"
<label htmlFor="fld-3-topic" className="block text-sm font-semibold text-stone-700 mb-2">Topic</label>
<select id="fld-3-topic" aria-label="Select"
value={form.topic}
onChange={(e) => setForm({ ...form, topic: e.target.value })}
className="w-full rounded-xl border-2 border-stone-200 bg-white px-5 py-4 text-base text-stone-900 outline-none focus:border-blue-400 focus:ring-2 focus:ring-blue-400/20 transition-colors appearance-none"
@@ -196,8 +196,8 @@ export default function IndianRiverContactPage() {
</select>
</div>
<div>
<label className="block text-sm font-semibold text-stone-700 mb-2">Message</label>
<textarea aria-label="How Can We Help You?"
<label htmlFor="fld-4-message" className="block text-sm font-semibold text-stone-700 mb-2">Message</label>
<textarea id="fld-4-message" aria-label="How Can We Help You?"
required
rows={5}
value={form.message}
+4 -8
View File
@@ -96,10 +96,8 @@ export default function ResetPasswordPage() {
)}
<div>
<label className="block text-sm font-medium text-slate-700">
New Password
</label>
<input aria-label=". 8 Characters"
<label htmlFor="fld-1-new-password" className="block text-sm font-medium text-slate-700">New Password</label>
<input id="fld-1-new-password" aria-label=". 8 Characters"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
@@ -111,10 +109,8 @@ export default function ResetPasswordPage() {
</div>
<div>
<label className="block text-sm font-medium text-slate-700">
Confirm Password
</label>
<input aria-label="Repeat Password"
<label htmlFor="fld-2-confirm-password" className="block text-sm font-medium text-slate-700">Confirm Password</label>
<input id="fld-2-confirm-password" aria-label="Repeat Password"
type="password"
value={confirm}
onChange={(e) => setConfirm(e.target.value)}
+8 -8
View File
@@ -139,8 +139,8 @@ export default function TuxedoContactPage() {
<form onSubmit={handleSubmit} className="space-y-6">
<div className="grid gap-6 md:grid-cols-2">
<div>
<label className="block text-sm font-semibold text-stone-700 mb-2">Your Name</label>
<input aria-label="Jane Smith"
<label htmlFor="fld-1-your-name" className="block text-sm font-semibold text-stone-700 mb-2">Your Name</label>
<input id="fld-1-your-name" aria-label="Jane Smith"
required
value={form.name}
onChange={(e) => setForm({ ...form, name: e.target.value })}
@@ -149,8 +149,8 @@ export default function TuxedoContactPage() {
/>
</div>
<div>
<label className="block text-sm font-semibold text-stone-700 mb-2">Email</label>
<input aria-label="Jane@example.com"
<label htmlFor="fld-2-email" className="block text-sm font-semibold text-stone-700 mb-2">Email</label>
<input id="fld-2-email" aria-label="Jane@example.com"
required
type="email"
value={form.email}
@@ -161,8 +161,8 @@ export default function TuxedoContactPage() {
</div>
</div>
<div>
<label className="block text-sm font-semibold text-stone-700 mb-2">Topic</label>
<select aria-label="Select"
<label htmlFor="fld-3-topic" className="block text-sm font-semibold text-stone-700 mb-2">Topic</label>
<select id="fld-3-topic" aria-label="Select"
value={form.topic}
onChange={(e) => setForm({ ...form, topic: e.target.value })}
className="w-full rounded-xl border border-stone-200 bg-white px-5 py-4 text-sm text-stone-900 outline-none focus:border-stone-900 focus:ring-1 focus:ring-stone-900 transition-colors appearance-none"
@@ -174,8 +174,8 @@ export default function TuxedoContactPage() {
</select>
</div>
<div>
<label className="block text-sm font-semibold text-stone-700 mb-2">Message</label>
<textarea aria-label="How Can We Help You?"
<label htmlFor="fld-4-message" className="block text-sm font-semibold text-stone-700 mb-2">Message</label>
<textarea id="fld-4-message" aria-label="How Can We Help You?"
required
rows={5}
value={form.message}
@@ -360,32 +360,26 @@ function OrderCard({
</button>
)}
{Number(order.balance_due) > 0 && order.fulfillment_status !== "fulfilled" && (
<button
<button aria-label="Record deposit"
type="button"
onClick={onRecordDeposit}
title="Record deposit"
className="inline-flex items-center gap-1.5 rounded-xl bg-purple-600 px-3 py-2 text-xs font-bold text-white shadow-sm hover:bg-purple-700 transition-colors"
>
onClick={onRecordDeposit} title="Record deposit"
className="inline-flex items-center gap-1.5 rounded-xl bg-purple-600 px-3 py-2 text-xs font-bold text-white shadow-sm hover:bg-purple-700 transition-colors">
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}><path strokeLinecap="round" strokeLinejoin="round" d="M12 6v12m-9-9h18" /></svg>
Deposit
</button>
)}
<a
<a aria-label="Download Invoice (PDF)"
href={`/api/wholesale/invoice/${order.id}/pdf`}
target="_blank"
rel="noopener noreferrer"
title="Download Invoice (PDF)"
className="inline-flex items-center justify-center rounded-xl w-9 h-9 text-slate-500 hover:text-slate-800 hover:bg-slate-100 transition-colors"
>
rel="noopener noreferrer" title="Download Invoice (PDF)"
className="inline-flex items-center justify-center rounded-xl w-9 h-9 text-slate-500 hover:text-slate-800 hover:bg-slate-100 transition-colors">
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75}><path strokeLinecap="round" strokeLinejoin="round" d="M9 12h9m-9-6h6m-3 12a9 9 0 110 12H15M6 2h9l4 4v14a2 2 0 01-2 2H6a2 2 0 01-2-2V4a2 2 0 012-2z"/></svg>
</a>
<div className="relative">
<button
<button aria-label="More actions"
type="button"
onClick={onToggleActions}
title="More actions"
className="inline-flex items-center justify-center rounded-xl w-9 h-9 text-slate-400 hover:text-slate-700 hover:bg-slate-100 transition-colors"
>
onClick={onToggleActions} title="More actions"
className="inline-flex items-center justify-center rounded-xl w-9 h-9 text-slate-400 hover:text-slate-700 hover:bg-slate-100 transition-colors">
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20"><circle cx="10" cy="4" r="1.5"/><circle cx="10" cy="10" r="1.5"/><circle cx="10" cy="16" r="1.5"/></svg>
</button>
{openActions === order.id && (
+4 -6
View File
@@ -248,10 +248,8 @@ export default function AIProviderPanel({ brandId }: Props) {
</div>
{provider === "openai" && (
<div>
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">
Organization ID (optional)
</label>
<input aria-label="Org ..."
<label htmlFor="fld-1-organization-id-optional" className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">Organization ID (optional)</label>
<input id="fld-1-organization-id-optional" aria-label="Org ..."
type="text"
value={orgId}
onChange={(e) => setOrgId(e.target.value)}
@@ -293,8 +291,8 @@ export default function AIProviderPanel({ brandId }: Props) {
</div>
</div>
<div>
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">Base URL</label>
<input aria-label="Https://api.openai.com/v1 Or Http://localhost:11434/v1"
<label htmlFor="fld-2-base-url" className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">Base URL</label>
<input id="fld-2-base-url" aria-label="Https://api.openai.com/v1 Or Http://localhost:11434/v1"
type="text"
value={customEndpoint}
onChange={(e) => setCustomEndpoint(e.target.value)}
+18 -18
View File
@@ -118,8 +118,8 @@ export default function AddLocationModal({ isOpen, onClose, brandId, onSuccess }
)}
<div className="space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Venue name *</label>
<input aria-label="Tractor Supply"
<label htmlFor="fld-1-venue-name" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Venue name *</label>
<input id="fld-1-venue-name" aria-label="Tractor Supply"
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
@@ -133,8 +133,8 @@ export default function AddLocationModal({ isOpen, onClose, brandId, onSuccess }
</div>
<div className="space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Street address</label>
<input aria-label="13778 E I 25 Frontage Rd"
<label htmlFor="fld-2-street-address" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Street address</label>
<input id="fld-2-street-address" aria-label="13778 E I 25 Frontage Rd"
type="text"
value={address}
onChange={(e) => setAddress(e.target.value)}
@@ -148,8 +148,8 @@ export default function AddLocationModal({ isOpen, onClose, brandId, onSuccess }
<div className="grid grid-cols-6 gap-4">
<div className="col-span-3 space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">City</label>
<input aria-label="Wellington"
<label htmlFor="fld-3-city" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">City</label>
<input id="fld-3-city" aria-label="Wellington"
type="text"
value={city}
onChange={(e) => setCity(e.target.value)}
@@ -161,8 +161,8 @@ export default function AddLocationModal({ isOpen, onClose, brandId, onSuccess }
/>
</div>
<div className="col-span-1 space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">State</label>
<input aria-label="CO"
<label htmlFor="fld-4-state" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">State</label>
<input id="fld-4-state" aria-label="CO"
type="text"
value={stateVal}
onChange={(e) => setStateVal(e.target.value.toUpperCase())}
@@ -175,8 +175,8 @@ export default function AddLocationModal({ isOpen, onClose, brandId, onSuccess }
/>
</div>
<div className="col-span-2 space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">ZIP</label>
<input aria-label="80549"
<label htmlFor="fld-5-zip" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">ZIP</label>
<input id="fld-5-zip" aria-label="80549"
type="text"
value={zip}
onChange={(e) => setZip(e.target.value)}
@@ -191,8 +191,8 @@ export default function AddLocationModal({ isOpen, onClose, brandId, onSuccess }
<div className="grid grid-cols-2 gap-4">
<div className="space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Phone</label>
<input aria-label="(970) 555 1234"
<label htmlFor="fld-6-phone" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Phone</label>
<input id="fld-6-phone" aria-label="(970) 555 1234"
type="tel"
value={phone}
onChange={(e) => setPhone(e.target.value)}
@@ -204,8 +204,8 @@ export default function AddLocationModal({ isOpen, onClose, brandId, onSuccess }
/>
</div>
<div className="space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Contact name</label>
<input aria-label="Store Manager"
<label htmlFor="fld-7-contact-name" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Contact name</label>
<input id="fld-7-contact-name" aria-label="Store Manager"
type="text"
value={contactName}
onChange={(e) => setContactName(e.target.value)}
@@ -219,8 +219,8 @@ export default function AddLocationModal({ isOpen, onClose, brandId, onSuccess }
</div>
<div className="space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Contact email</label>
<input aria-label="Manager@example.com"
<label htmlFor="fld-8-contact-email" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Contact email</label>
<input id="fld-8-contact-email" aria-label="Manager@example.com"
type="email"
value={contactEmail}
onChange={(e) => setContactEmail(e.target.value)}
@@ -233,8 +233,8 @@ export default function AddLocationModal({ isOpen, onClose, brandId, onSuccess }
</div>
<div className="space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Notes</label>
<textarea aria-label="Park On West Side. Use Side Entrance After 9am."
<label htmlFor="fld-9-notes" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Notes</label>
<textarea id="fld-9-notes" aria-label="Park On West Side. Use Side Entrance After 9am."
value={notes}
onChange={(e) => setNotes(e.target.value)}
placeholder="Park on west side. Use side entrance after 9am."
@@ -221,8 +221,8 @@ export default function AdvancedIntegrations({ brandId, brands }: Props) {
</div>
{/* From Email */}
<div>
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">From Email</label>
<input aria-label="Orders@yourbrand.com"
<label htmlFor="fld-1-from-email" className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">From Email</label>
<input id="fld-1-from-email" aria-label="Orders@yourbrand.com"
type="email"
value={credentials.resend.from_email}
onChange={(e) => setCredentials((p) => ({
@@ -235,8 +235,8 @@ export default function AdvancedIntegrations({ brandId, brands }: Props) {
</div>
{/* From Name */}
<div>
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">From Name</label>
<input aria-label="Your Brand Name"
<label htmlFor="fld-2-from-name" className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">From Name</label>
<input id="fld-2-from-name" aria-label="Your Brand Name"
type="text"
value={credentials.resend.from_name}
onChange={(e) => setCredentials((p) => ({
@@ -293,8 +293,8 @@ export default function AdvancedIntegrations({ brandId, brands }: Props) {
<div className="p-4 sm:p-6 space-y-4">
{/* Account SID */}
<div>
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">Account SID</label>
<input aria-label="AC..."
<label htmlFor="fld-3-account-sid" className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">Account SID</label>
<input id="fld-3-account-sid" aria-label="AC..."
type="text"
value={credentials.twilio.account_sid}
onChange={(e) => setCredentials((p) => ({
@@ -330,8 +330,8 @@ export default function AdvancedIntegrations({ brandId, brands }: Props) {
</div>
{/* Phone Number */}
<div>
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">Phone Number</label>
<input aria-label="+1234567890"
<label htmlFor="fld-4-phone-number" className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">Phone Number</label>
<input id="fld-4-phone-number" aria-label="+1234567890"
type="tel"
value={credentials.twilio.phone_number}
onChange={(e) => setCredentials((p) => ({
+4 -4
View File
@@ -105,8 +105,8 @@ export default function AdvancedShipping({ brandId }: { brandId: string }) {
<div className="p-4 sm:p-6 space-y-4">
{/* Carrier Selection */}
<div>
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">Carrier</label>
<select aria-label="Select"
<label htmlFor="fld-1-carrier" className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">Carrier</label>
<select id="fld-1-carrier" aria-label="Select"
value={carrier}
onChange={(e) => setCarrier(e.target.value)}
className="w-full sm:w-auto rounded-lg border border-[var(--admin-border)] bg-white px-4 py-2.5 text-sm text-[var(--admin-text-primary)] outline-none focus:border-emerald-500 focus:ring-1 focus:ring-emerald-500"
@@ -120,8 +120,8 @@ export default function AdvancedShipping({ brandId }: { brandId: string }) {
{/* Account Number */}
<div>
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">Account Number</label>
<input aria-label="Enter Account Number"
<label htmlFor="fld-2-account-number" className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">Account Number</label>
<input id="fld-2-account-number" aria-label="Enter Account Number"
type="text"
value={accountNumber}
onChange={(e) => setAccountNumber(e.target.value)}
+6 -6
View File
@@ -96,8 +96,8 @@ export default function AdvancedSquareSync({ brandId }: { brandId: string }) {
<div className="p-4 sm:p-6 space-y-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">Application ID</label>
<input aria-label="Sq0idp ..."
<label htmlFor="fld-1-application-id" className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">Application ID</label>
<input id="fld-1-application-id" aria-label="Sq0idp ..."
type="password"
value={appId}
onChange={(e) => setAppId(e.target.value)}
@@ -106,8 +106,8 @@ export default function AdvancedSquareSync({ brandId }: { brandId: string }) {
/>
</div>
<div>
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">Access Token</label>
<input aria-label="EAAAl..."
<label htmlFor="fld-2-access-token" className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">Access Token</label>
<input id="fld-2-access-token" aria-label="EAAAl..."
type="password"
value={accessToken}
onChange={(e) => setAccessToken(e.target.value)}
@@ -116,8 +116,8 @@ export default function AdvancedSquareSync({ brandId }: { brandId: string }) {
/>
</div>
<div>
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">Location ID</label>
<input aria-label="L..."
<label htmlFor="fld-3-location-id" className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1.5">Location ID</label>
<input id="fld-3-location-id" aria-label="L..."
type="text"
value={locationId}
onChange={(e) => setLocationId(e.target.value)}
+24 -28
View File
@@ -147,10 +147,8 @@ function NewCampaignModal({
)}
<div>
<label className="block text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">
Campaign Name *
</label>
<input aria-label=". Weekly Pickup Reminder"
<label htmlFor="fld-1-campaign-name" className="block text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Campaign Name *</label>
<input id="fld-1-campaign-name" aria-label=". Weekly Pickup Reminder"
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
@@ -161,10 +159,8 @@ function NewCampaignModal({
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">
Campaign Type *
</label>
<select aria-label="Select"
<label htmlFor="fld-2-campaign-type" className="block text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Campaign Type *</label>
<select id="fld-2-campaign-type" aria-label="Select"
value={campaignType}
onChange={(e) => setCampaignType(e.target.value as CampaignType)}
className="w-full border border-[var(--admin-border)] rounded-lg px-3 py-2.5 text-sm bg-white text-[var(--admin-text-primary)] focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 outline-none"
@@ -504,8 +500,8 @@ export function CampaignEditPanel({
<div className="rounded-xl border border-[var(--admin-border)] bg-white p-4 sm:p-6 space-y-4">
<h3 className="text-xs sm:text-sm font-semibold text-[var(--admin-text-muted)] uppercase tracking-wide">Basic Info</h3>
<div>
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Campaign Name *</label>
<input aria-label=". Tuxedo Stop 5/15 Reminder"
<label htmlFor="fld-3-campaign-name-2" className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Campaign Name *</label>
<input id="fld-3-campaign-name-2" aria-label=". Tuxedo Stop 5/15 Reminder"
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
@@ -515,8 +511,8 @@ export function CampaignEditPanel({
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Campaign Type *</label>
<select aria-label="Select"
<label htmlFor="fld-4-campaign-type-2" className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Campaign Type *</label>
<select id="fld-4-campaign-type-2" aria-label="Select"
value={campaignType}
onChange={(e) => setCampaignType(e.target.value as CampaignType)}
className="w-full border border-[var(--admin-border)] rounded-lg px-3 py-2 text-sm bg-white text-[var(--admin-text-primary)]"
@@ -527,8 +523,8 @@ export function CampaignEditPanel({
</select>
</div>
<div>
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Template</label>
<select aria-label="Select"
<label htmlFor="fld-5-template" className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Template</label>
<select id="fld-5-template" aria-label="Select"
value={templateId}
onChange={(e) => {
setTemplateId(e.target.value);
@@ -567,8 +563,8 @@ export function CampaignEditPanel({
)}
</div>
<div>
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Target By</label>
<select aria-label="Select"
<label htmlFor="fld-6-target-by" className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Target By</label>
<select id="fld-6-target-by" aria-label="Select"
value={audienceTarget}
onChange={(e) => {
setAudienceTarget(e.target.value);
@@ -585,8 +581,8 @@ export function CampaignEditPanel({
{audienceTarget === "stop" && (
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
<div>
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Stop ID</label>
<input aria-label="UUID"
<label htmlFor="fld-7-stop-id" className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Stop ID</label>
<input id="fld-7-stop-id" aria-label="UUID"
type="text"
value={stopId}
onChange={(e) => setStopId(e.target.value)}
@@ -595,8 +591,8 @@ export function CampaignEditPanel({
/>
</div>
<div>
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">From</label>
<input aria-label="Date"
<label htmlFor="fld-8-from" className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">From</label>
<input id="fld-8-from" aria-label="Date"
type="date"
value={dateFrom}
onChange={(e) => setDateFrom(e.target.value)}
@@ -604,8 +600,8 @@ export function CampaignEditPanel({
/>
</div>
<div>
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">To</label>
<input aria-label="Date"
<label htmlFor="fld-9-to" className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">To</label>
<input id="fld-9-to" aria-label="Date"
type="date"
value={dateTo}
onChange={(e) => setDateTo(e.target.value)}
@@ -638,8 +634,8 @@ export function CampaignEditPanel({
<div className="rounded-xl border border-[var(--admin-border)] bg-white p-4 sm:p-6 space-y-4">
<h3 className="text-xs sm:text-sm font-semibold text-[var(--admin-text-muted)] uppercase tracking-wide">Message Content</h3>
<div>
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Subject</label>
<input aria-label="Email Subject Line"
<label htmlFor="fld-10-subject" className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Subject</label>
<input id="fld-10-subject" aria-label="Email Subject Line"
type="text"
value={subject}
onChange={(e) => setSubject(e.target.value)}
@@ -648,8 +644,8 @@ export function CampaignEditPanel({
/>
</div>
<div>
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Body (Plain Text)</label>
<textarea aria-label="Message Content..."
<label htmlFor="fld-11-body-plain-text" className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Body (Plain Text)</label>
<textarea id="fld-11-body-plain-text" aria-label="Message Content..."
value={bodyText}
onChange={(e) => setBodyText(e.target.value)}
rows={6}
@@ -684,8 +680,8 @@ export function CampaignEditPanel({
</div>
{scheduleMode === "later" && (
<div>
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Send date & time</label>
<input aria-label="Datetime Local"
<label htmlFor="fld-12-send-date-time" className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Send date & time</label>
<input id="fld-12-send-date-time" aria-label="Datetime Local"
type="datetime-local"
value={scheduledAt}
onChange={(e) => setScheduledAt(e.target.value)}
+18 -18
View File
@@ -140,8 +140,8 @@ export default function EditLocationModal({ isOpen, onClose, location, brandId,
)}
<div className="space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Venue name *</label>
<input aria-label="Text"
<label htmlFor="fld-1-venue-name" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Venue name *</label>
<input id="fld-1-venue-name" aria-label="Text"
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
@@ -154,8 +154,8 @@ export default function EditLocationModal({ isOpen, onClose, location, brandId,
</div>
<div className="space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Street address</label>
<input aria-label="Text"
<label htmlFor="fld-2-street-address" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Street address</label>
<input id="fld-2-street-address" aria-label="Text"
type="text"
value={address}
onChange={(e) => setAddress(e.target.value)}
@@ -168,8 +168,8 @@ export default function EditLocationModal({ isOpen, onClose, location, brandId,
<div className="grid grid-cols-6 gap-4">
<div className="col-span-3 space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">City</label>
<input aria-label="Text"
<label htmlFor="fld-3-city" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">City</label>
<input id="fld-3-city" aria-label="Text"
type="text"
value={city}
onChange={(e) => setCity(e.target.value)}
@@ -180,8 +180,8 @@ export default function EditLocationModal({ isOpen, onClose, location, brandId,
/>
</div>
<div className="col-span-1 space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">State</label>
<input aria-label="Text"
<label htmlFor="fld-4-state" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">State</label>
<input id="fld-4-state" aria-label="Text"
type="text"
value={stateVal}
onChange={(e) => setStateVal(e.target.value.toUpperCase())}
@@ -193,8 +193,8 @@ export default function EditLocationModal({ isOpen, onClose, location, brandId,
/>
</div>
<div className="col-span-2 space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">ZIP</label>
<input aria-label="Text"
<label htmlFor="fld-5-zip" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">ZIP</label>
<input id="fld-5-zip" aria-label="Text"
type="text"
value={zip}
onChange={(e) => setZip(e.target.value)}
@@ -208,8 +208,8 @@ export default function EditLocationModal({ isOpen, onClose, location, brandId,
<div className="grid grid-cols-2 gap-4">
<div className="space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Phone</label>
<input aria-label="Tel"
<label htmlFor="fld-6-phone" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Phone</label>
<input id="fld-6-phone" aria-label="Tel"
type="tel"
value={phone}
onChange={(e) => setPhone(e.target.value)}
@@ -220,8 +220,8 @@ export default function EditLocationModal({ isOpen, onClose, location, brandId,
/>
</div>
<div className="space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Contact name</label>
<input aria-label="Text"
<label htmlFor="fld-7-contact-name" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Contact name</label>
<input id="fld-7-contact-name" aria-label="Text"
type="text"
value={contactName}
onChange={(e) => setContactName(e.target.value)}
@@ -234,8 +234,8 @@ export default function EditLocationModal({ isOpen, onClose, location, brandId,
</div>
<div className="space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Contact email</label>
<input aria-label="Email"
<label htmlFor="fld-8-contact-email" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Contact email</label>
<input id="fld-8-contact-email" aria-label="Email"
type="email"
value={contactEmail}
onChange={(e) => setContactEmail(e.target.value)}
@@ -247,8 +247,8 @@ export default function EditLocationModal({ isOpen, onClose, location, brandId,
</div>
<div className="space-y-1.5">
<label className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Notes</label>
<textarea aria-label="Textarea"
<label htmlFor="fld-9-notes" className="block text-xs font-medium text-stone-500 uppercase tracking-wide ml-1">Notes</label>
<textarea id="fld-9-notes" aria-label="Textarea"
value={notes}
onChange={(e) => setNotes(e.target.value)}
rows={2}
@@ -519,8 +519,8 @@ export default function CampaignComposerPage({ brandId, campaigns, templates, se
{/* Editor */}
<div className="space-y-4">
<div>
<label className="block text-sm font-semibold text-stone-700 mb-1.5">Subject Line</label>
<input aria-label="Enter A Compelling Subject..."
<label htmlFor="fld-1-subject-line" className="block text-sm font-semibold text-stone-700 mb-1.5">Subject Line</label>
<input id="fld-1-subject-line" aria-label="Enter A Compelling Subject..."
type="text"
placeholder="Enter a compelling subject..."
value={subject}
@@ -531,8 +531,8 @@ export default function CampaignComposerPage({ brandId, campaigns, templates, se
</div>
<div>
<label className="block text-sm font-semibold text-stone-700 mb-1.5">Message Body</label>
<textarea aria-label="Write Your Message Here..."
<label htmlFor="fld-2-message-body" className="block text-sm font-semibold text-stone-700 mb-1.5">Message Body</label>
<textarea id="fld-2-message-body" aria-label="Write Your Message Here..."
placeholder="Write your message here..."
value={bodyText}
onChange={(e) => setBodyText(e.target.value)}
@@ -596,8 +596,8 @@ export default function CampaignComposerPage({ brandId, campaigns, templates, se
</div>
<div>
<label className="block text-sm font-semibold text-stone-700 mb-1.5">Recipient Segment</label>
<select aria-label="Select"
<label htmlFor="fld-3-recipient-segment" className="block text-sm font-semibold text-stone-700 mb-1.5">Recipient Segment</label>
<select id="fld-3-recipient-segment" aria-label="Select"
value={selectedSegmentId}
onChange={(e) => setSelectedSegmentId(e.target.value)}
className="w-full border border-stone-200 rounded-xl px-4 py-3 text-sm bg-white outline-none focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 transition-all"
@@ -791,8 +791,8 @@ export default function CampaignComposerPage({ brandId, campaigns, templates, se
{!sendNow && (
<div className="mt-4">
<label className="block text-sm font-semibold text-stone-700 mb-1.5">Send Date & Time</label>
<input aria-label="Datetime Local"
<label htmlFor="fld-4-send-date-time" className="block text-sm font-semibold text-stone-700 mb-1.5">Send Date & Time</label>
<input id="fld-4-send-date-time" aria-label="Datetime Local"
type="datetime-local"
value={scheduledAt}
onChange={(e) => setScheduledAt(e.target.value)}
@@ -327,8 +327,8 @@ function FilterBlock({ brandId, filter, onChange, onRemove }: FilterBlockProps)
</select>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-xs text-[var(--admin-text-muted)] mb-1 block">From</label>
<input aria-label="Date"
<label htmlFor="fld-1-from" className="text-xs text-[var(--admin-text-muted)] mb-1 block">From</label>
<input id="fld-1-from" aria-label="Date"
type="date"
value={filter.params.date_from ?? ""}
onChange={(e) => onChange({ params: { ...filter.params, date_from: e.target.value } })}
@@ -336,8 +336,8 @@ function FilterBlock({ brandId, filter, onChange, onRemove }: FilterBlockProps)
/>
</div>
<div>
<label className="text-xs text-[var(--admin-text-muted)] mb-1 block">To</label>
<input aria-label="Date"
<label htmlFor="fld-2-to" className="text-xs text-[var(--admin-text-muted)] mb-1 block">To</label>
<input id="fld-2-to" aria-label="Date"
type="date"
value={filter.params.date_to ?? ""}
onChange={(e) => onChange({ params: { ...filter.params, date_to: e.target.value } })}
@@ -366,8 +366,8 @@ function FilterBlock({ brandId, filter, onChange, onRemove }: FilterBlockProps)
))}
</select>
<div className="flex items-center gap-2">
<label className="text-xs text-[var(--admin-text-muted)] whitespace-nowrap">In the last</label>
<input aria-label="Number"
<label htmlFor="fld-3-in-the-last" className="text-xs text-[var(--admin-text-muted)] whitespace-nowrap">In the last</label>
<input id="fld-3-in-the-last" aria-label="Number"
type="number"
min={1}
max={365}
@@ -422,8 +422,8 @@ function FilterBlock({ brandId, filter, onChange, onRemove }: FilterBlockProps)
))}
</select>
<div className="flex items-center gap-2">
<label className="text-xs text-[var(--admin-text-muted)] whitespace-nowrap">In the last</label>
<input aria-label="Number"
<label htmlFor="fld-4-in-the-last-2" className="text-xs text-[var(--admin-text-muted)] whitespace-nowrap">In the last</label>
<input id="fld-4-in-the-last-2" aria-label="Number"
type="number"
min={1}
max={365}
@@ -74,8 +74,8 @@ export default function SegmentEditModal({ initialName = "", initialDescription
{/* Content */}
<div className="p-6 flex flex-col gap-4">
<div>
<label className="block text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Segment Name</label>
<input aria-label=". Fort Pierce Regulars"
<label htmlFor="fld-1-segment-name" className="block text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Segment Name</label>
<input id="fld-1-segment-name" aria-label=". Fort Pierce Regulars"
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
@@ -217,10 +217,8 @@ export default function MessageCustomersSection({
<form onSubmit={handleSend} className="space-y-4">
{showSubject && (
<div>
<label className="mb-1 block text-sm font-medium text-zinc-300">
Subject
</label>
<input aria-label="Important Update About Your Pickup"
<label htmlFor="fld-1-subject" className="mb-1 block text-sm font-medium text-zinc-300">Subject</label>
<input id="fld-1-subject" aria-label="Important Update About Your Pickup"
type="text"
value={subject}
onChange={(e) => setSubject(e.target.value)}
@@ -231,10 +229,8 @@ export default function MessageCustomersSection({
)}
<div>
<label className="mb-1 block text-sm font-medium text-zinc-300">
Message
</label>
<textarea aria-label="Type Your Message Here..."
<label htmlFor="fld-2-message" className="mb-1 block text-sm font-medium text-zinc-300">Message</label>
<textarea id="fld-2-message" aria-label="Type Your Message Here..."
value={body}
onChange={(e) => setBody(e.target.value)}
rows={4}
+6 -6
View File
@@ -279,8 +279,8 @@ export default function NewStopForm({ duplicateFrom }: Props) {
</div>
<div>
<label className="block text-xs font-semibold text-stone-700 mb-1.5">Active</label>
<select aria-label="Select"
<label htmlFor="fld-1-active" className="block text-xs font-semibold text-stone-700 mb-1.5">Active</label>
<select id="fld-1-active" aria-label="Select"
value={active}
onChange={(e) => setActive(e.target.value)}
className="w-full rounded-xl border border-[var(--admin-border)] bg-white px-3 py-2.5 text-sm text-[var(--admin-text-primary)] outline-none focus:border-[var(--admin-accent)] focus:ring-2 focus:ring-[var(--admin-accent)]/20"
@@ -292,8 +292,8 @@ export default function NewStopForm({ duplicateFrom }: Props) {
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-xs font-semibold text-stone-700 mb-1.5">Street Address</label>
<input aria-label="123 Main St"
<label htmlFor="fld-2-street-address" className="block text-xs font-semibold text-stone-700 mb-1.5">Street Address</label>
<input id="fld-2-street-address" aria-label="123 Main St"
type="text"
value={address}
onChange={(e) => setAddress(e.target.value)}
@@ -303,8 +303,8 @@ export default function NewStopForm({ duplicateFrom }: Props) {
</div>
<div>
<label className="block text-xs font-semibold text-stone-700 mb-1.5">ZIP Code</label>
<input aria-label="80102"
<label htmlFor="fld-3-zip-code" className="block text-xs font-semibold text-stone-700 mb-1.5">ZIP Code</label>
<input id="fld-3-zip-code" aria-label="80102"
type="text"
value={zip}
onChange={(e) => setZip(e.target.value)}
+4 -4
View File
@@ -371,8 +371,8 @@ export default function OrderEditForm({ order, brandId }: OrderEditFormProps) {
<div className="mt-3 grid grid-cols-1 sm:grid-cols-2 gap-3">
<div>
<label className="ha-field-label mb-1">Qty</label>
<input aria-label="Number"
<label htmlFor="fld-1-qty" className="ha-field-label mb-1">Qty</label>
<input id="fld-1-qty" aria-label="Number"
type="number"
min="1"
value={item.quantity}
@@ -461,8 +461,8 @@ export default function OrderEditForm({ order, brandId }: OrderEditFormProps) {
)}
</div>
<div>
<label className="ha-field-label mb-1.5">Discount Reason</label>
<input aria-label="Optional"
<label htmlFor="fld-2-discount-reason" className="ha-field-label mb-1.5">Discount Reason</label>
<input id="fld-2-discount-reason" aria-label="Optional"
type="text"
value={discount_reason}
onChange={(e) => setDiscount_reason(e.target.value)}
+8 -8
View File
@@ -170,8 +170,8 @@ export default function OrderPaymentSection({
<div className="grid grid-cols-2 gap-4">
<div>
<label className="mb-1 block text-xs font-semibold text-stone-500">Processor</label>
<select aria-label="Select"
<label htmlFor="fld-1-processor" className="mb-1 block text-xs font-semibold text-stone-500">Processor</label>
<select id="fld-1-processor" aria-label="Select"
value={processor}
onChange={(e) => setProcessor(e.target.value)}
className="w-full rounded-xl border border-stone-200 bg-white px-3 py-2.5 text-sm outline-none focus:border-[var(--admin-accent)] focus:ring-2 focus:ring-[var(--admin-accent)]/20"
@@ -186,8 +186,8 @@ export default function OrderPaymentSection({
</div>
<div>
<label className="mb-1 block text-xs font-semibold text-stone-500">Payment Status</label>
<select aria-label="Select"
<label htmlFor="fld-2-payment-status" className="mb-1 block text-xs font-semibold text-stone-500">Payment Status</label>
<select id="fld-2-payment-status" aria-label="Select"
value={status}
onChange={(e) => setStatus(e.target.value)}
className="w-full rounded-xl border border-stone-200 bg-white px-3 py-2.5 text-sm outline-none focus:border-[var(--admin-accent)] focus:ring-2 focus:ring-[var(--admin-accent)]/20"
@@ -202,8 +202,8 @@ export default function OrderPaymentSection({
</div>
<div>
<label className="mb-1 block text-xs font-semibold text-stone-500">Transaction ID</label>
<input aria-label="External Payment Reference"
<label htmlFor="fld-3-transaction-id" className="mb-1 block text-xs font-semibold text-stone-500">Transaction ID</label>
<input id="fld-3-transaction-id" aria-label="External Payment Reference"
type="text"
value={transactionId}
onChange={(e) => setTransactionId(e.target.value)}
@@ -300,8 +300,8 @@ export default function OrderPaymentSection({
)}
</div>
<div>
<label className="mb-1 block text-xs font-semibold text-stone-500">Reason</label>
<input aria-label="Customer Request, Defective Product, Etc."
<label htmlFor="fld-4-reason" className="mb-1 block text-xs font-semibold text-stone-500">Reason</label>
<input id="fld-4-reason" aria-label="Customer Request, Defective Product, Etc."
type="text"
value={refundReason}
onChange={(e) => setRefundReason(e.target.value)}
@@ -359,6 +359,7 @@ export default function ProductFormModal({
src={imagePreview}
alt="Product preview"
fill
sizes="(max-width: 640px) 100vw, 50vw"
style={{ objectFit: "contain" }}
className="rounded-md"
/>
@@ -160,6 +160,7 @@ function ProductRowBase({
src={product.image_url}
alt={product.name}
fill
sizes="48px"
style={{ objectFit: "cover" }}
onError={(e) => { (e.target as HTMLImageElement).style.display = "none"; }}
/>
+2
View File
@@ -462,6 +462,7 @@ function TableView({
src={product.image_url}
alt={product.name}
fill
sizes="40px"
style={{ objectFit: "cover" }}
onError={(e) => { (e.target as HTMLImageElement).style.display = "none"; }}
/>
@@ -623,6 +624,7 @@ function CardView({
src={product.image_url}
alt={product.name}
fill
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
style={{ objectFit: "cover" }}
className="transition-transform group-hover:scale-105"
onError={(e) => { (e.target as HTMLImageElement).style.display = "none"; }}
+16 -16
View File
@@ -323,8 +323,8 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
<h3 className="text-sm font-semibold text-stone-800 mb-4">Pay Period & Overtime</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-2">Work week starts on</label>
<select aria-label="Select" value={payPeriodStartDay} onChange={e => setPayPeriodStartDay(Number(e.target.value))}
<label htmlFor="fld-1-work-week-starts-on" className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-2">Work week starts on</label>
<select id="fld-1-work-week-starts-on" aria-label="Select" value={payPeriodStartDay} onChange={e => setPayPeriodStartDay(Number(e.target.value))}
className="w-full px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 focus:outline-none focus:border-emerald-500 transition-colors">
{DAYS.map((d, i) => <option key={d} value={i}>{d}</option>)}
</select>
@@ -630,23 +630,23 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
</div>
)}
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Name *</label>
<input aria-label="Worker Name" value={workerName} onChange={e => setWorkerName(e.target.value)}
<label htmlFor="fld-2-name" className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Name *</label>
<input id="fld-2-name" aria-label="Worker Name" value={workerName} onChange={e => setWorkerName(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 placeholder:text-stone-400 focus:outline-none focus:border-emerald-500 transition-colors"
placeholder="Worker name" />
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Role</label>
<select aria-label="Select" value={workerRole} onChange={e => setWorkerRole(e.target.value)}
<label htmlFor="fld-3-role" className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Role</label>
<select id="fld-3-role" aria-label="Select" value={workerRole} onChange={e => setWorkerRole(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 focus:outline-none focus:border-emerald-500 transition-colors">
<option value="worker">Worker</option>
<option value="time_admin">Time Admin</option>
</select>
</div>
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Lang</label>
<select aria-label="Select" value={workerLang} onChange={e => setWorkerLang(e.target.value)}
<label htmlFor="fld-4-lang" className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Lang</label>
<select id="fld-4-lang" aria-label="Select" value={workerLang} onChange={e => setWorkerLang(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 focus:outline-none focus:border-emerald-500 transition-colors">
<option value="en">English</option>
<option value="es">Español</option>
@@ -695,21 +695,21 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
<div className="p-6 space-y-4">
{taskError && <div className="bg-red-50 border border-red-200 rounded-xl py-3 px-4 text-red-700 text-sm">{taskError}</div>}
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Name (EN) *</label>
<input aria-label=". Harvesting" value={taskName} onChange={e => setTaskName(e.target.value)}
<label htmlFor="fld-5-name-en" className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Name (EN) *</label>
<input id="fld-5-name-en" aria-label=". Harvesting" value={taskName} onChange={e => setTaskName(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 placeholder:text-stone-400 focus:outline-none focus:border-emerald-500 transition-colors"
placeholder="e.g. Harvesting" />
</div>
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Name (ES)</label>
<input aria-label=". Cosecha" value={taskNameEs} onChange={e => setTaskNameEs(e.target.value)}
<label htmlFor="fld-6-name-es" className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Name (ES)</label>
<input id="fld-6-name-es" aria-label=". Cosecha" value={taskNameEs} onChange={e => setTaskNameEs(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 placeholder:text-stone-400 focus:outline-none focus:border-emerald-500 transition-colors"
placeholder="e.g. Cosecha" />
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Unit</label>
<select aria-label="Select" value={taskUnit} onChange={e => setTaskUnit(e.target.value)}
<label htmlFor="fld-7-unit" className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Unit</label>
<select id="fld-7-unit" aria-label="Select" value={taskUnit} onChange={e => setTaskUnit(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 focus:outline-none focus:border-emerald-500 transition-colors">
<option value="hours">Hours</option>
<option value="pieces">Pieces</option>
@@ -717,8 +717,8 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
</select>
</div>
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Sort Order</label>
<input aria-label="Number" type="number" value={taskSortOrder} onChange={e => setTaskSortOrder(parseInt(e.target.value) || 0)}
<label htmlFor="fld-8-sort-order" className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Sort Order</label>
<input id="fld-8-sort-order" aria-label="Number" type="number" value={taskSortOrder} onChange={e => setTaskSortOrder(parseInt(e.target.value) || 0)}
className="w-full px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 focus:outline-none focus:border-emerald-500 transition-colors" />
</div>
</div>
+8 -8
View File
@@ -262,8 +262,8 @@ export default function StopEditForm({ stop, brands, onSaved }: StopEditFormProp
</div>
<div>
<label className="block text-xs font-semibold text-stone-700 mb-1.5">Time</label>
<input aria-label=". 8:00 AM 2:00 PM"
<label htmlFor="fld-1-time" className="block text-xs font-semibold text-stone-700 mb-1.5">Time</label>
<input id="fld-1-time" aria-label=". 8:00 AM 2:00 PM"
type="text"
value={time}
onChange={(e) => setTime(e.target.value)}
@@ -274,8 +274,8 @@ export default function StopEditForm({ stop, brands, onSaved }: StopEditFormProp
</div>
<div>
<label className="block text-xs font-semibold text-stone-700 mb-1.5">Location Name</label>
<input aria-label="Street Address Or Intersection"
<label htmlFor="fld-2-location-name" className="block text-xs font-semibold text-stone-700 mb-1.5">Location Name</label>
<input id="fld-2-location-name" aria-label="Street Address Or Intersection"
type="text"
value={location}
onChange={(e) => setLocation(e.target.value)}
@@ -286,8 +286,8 @@ export default function StopEditForm({ stop, brands, onSaved }: StopEditFormProp
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-xs font-semibold text-stone-700 mb-1.5">Street Address</label>
<input aria-label="123 Main St"
<label htmlFor="fld-3-street-address" className="block text-xs font-semibold text-stone-700 mb-1.5">Street Address</label>
<input id="fld-3-street-address" aria-label="123 Main St"
type="text"
value={address}
onChange={(e) => setAddress(e.target.value)}
@@ -297,8 +297,8 @@ export default function StopEditForm({ stop, brands, onSaved }: StopEditFormProp
</div>
<div>
<label className="block text-xs font-semibold text-stone-700 mb-1.5">ZIP Code</label>
<input aria-label="80102"
<label htmlFor="fld-4-zip-code" className="block text-xs font-semibold text-stone-700 mb-1.5">ZIP Code</label>
<input id="fld-4-zip-code" aria-label="80102"
type="text"
value={zip}
onChange={(e) => setZip(e.target.value)}
+14 -18
View File
@@ -165,10 +165,8 @@ function NewTemplateModal({
)}
<div>
<label className="block text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">
Template Name *
</label>
<input aria-label=". Pickup Reminder"
<label htmlFor="fld-1-template-name" className="block text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Template Name *</label>
<input id="fld-1-template-name" aria-label=". Pickup Reminder"
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
@@ -179,10 +177,8 @@ function NewTemplateModal({
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">
Template Type *
</label>
<select aria-label="Select"
<label htmlFor="fld-2-template-type" className="block text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Template Type *</label>
<select id="fld-2-template-type" aria-label="Select"
value={templateType}
onChange={(e) => setTemplateType(e.target.value as TemplateType)}
className="w-full border border-[var(--admin-border)] rounded-lg px-3 py-2.5 text-sm bg-white text-[var(--admin-text-primary)] focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 outline-none"
@@ -445,8 +441,8 @@ export function TemplateEditForm({
</div>
</div>
<div>
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Template Name *</label>
<input aria-label=". Pickup Reminder"
<label htmlFor="fld-3-template-name-2" className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Template Name *</label>
<input id="fld-3-template-name-2" aria-label=". Pickup Reminder"
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
@@ -456,8 +452,8 @@ export function TemplateEditForm({
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Template Type *</label>
<select aria-label="Select"
<label htmlFor="fld-4-template-type-2" className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Template Type *</label>
<select id="fld-4-template-type-2" aria-label="Select"
value={templateType}
onChange={(e) => setTemplateType(e.target.value as TemplateType)}
className="w-full border border-[var(--admin-border)] rounded-lg px-3 py-2 text-sm bg-white text-[var(--admin-text-primary)]"
@@ -468,8 +464,8 @@ export function TemplateEditForm({
</select>
</div>
<div>
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Campaign Type</label>
<select aria-label="Select"
<label htmlFor="fld-5-campaign-type" className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Campaign Type</label>
<select id="fld-5-campaign-type" aria-label="Select"
value={campaignType}
onChange={(e) => setCampaignType(e.target.value as CampaignType)}
className="w-full border border-[var(--admin-border)] rounded-lg px-3 py-2 text-sm bg-white text-[var(--admin-text-primary)]"
@@ -487,8 +483,8 @@ export function TemplateEditForm({
<div className="rounded-xl border border-[var(--admin-border)] bg-white p-4 sm:p-6 space-y-4">
<h3 className="text-xs sm:text-sm font-semibold text-[var(--admin-text-muted)] uppercase tracking-wide">Subject Line</h3>
<div>
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Subject *</label>
<input aria-label="Email Subject Line"
<label htmlFor="fld-6-subject" className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Subject *</label>
<input id="fld-6-subject" aria-label="Email Subject Line"
type="text"
value={subject}
onChange={(e) => setSubject(e.target.value)}
@@ -548,8 +544,8 @@ export function TemplateEditForm({
{htmlMode ? (
<>
<div>
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">HTML Body</label>
<textarea aria-label="<p>HTML Version With Variables Like {{first Name}}...</p>"
<label htmlFor="fld-7-html-body" className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">HTML Body</label>
<textarea id="fld-7-html-body" aria-label="<p>HTML Version With Variables Like {{first Name}}...</p>"
value={bodyHtml}
onChange={(e) => setBodyHtml(e.target.value)}
rows={12}
+24 -24
View File
@@ -476,8 +476,8 @@ function SummaryTab({ summary, workers, loading, dateRange, setDateRange, onRefr
<div className="rounded-2xl border border-[var(--admin-border)] bg-white p-4">
<div className="flex flex-wrap gap-4 items-end">
<div className="space-y-1">
<label className="text-xs text-[var(--admin-text-muted)] font-medium">From</label>
<input aria-label="Date"
<label htmlFor="fld-1-from" className="text-xs text-[var(--admin-text-muted)] font-medium">From</label>
<input id="fld-1-from" aria-label="Date"
type="date"
value={dateRange.start}
onChange={(e) => setDateRange((prev) => ({ ...prev, start: e.target.value }))}
@@ -485,8 +485,8 @@ function SummaryTab({ summary, workers, loading, dateRange, setDateRange, onRefr
/>
</div>
<div className="space-y-1">
<label className="text-xs text-[var(--admin-text-muted)] font-medium">To</label>
<input aria-label="Date"
<label htmlFor="fld-2-to" className="text-xs text-[var(--admin-text-muted)] font-medium">To</label>
<input id="fld-2-to" aria-label="Date"
type="date"
value={dateRange.end}
onChange={(e) => setDateRange((prev) => ({ ...prev, end: e.target.value }))}
@@ -711,8 +711,8 @@ function LogsTab({ logs, workers, tasks, dateRange, setDateRange, selectedWorker
<div className="rounded-2xl border border-[var(--admin-border)] bg-white p-4">
<div className="flex flex-wrap gap-4 items-end">
<div className="space-y-1">
<label className="text-xs text-[var(--admin-text-muted)] font-medium">From</label>
<input aria-label="Date"
<label htmlFor="fld-3-from-2" className="text-xs text-[var(--admin-text-muted)] font-medium">From</label>
<input id="fld-3-from-2" aria-label="Date"
type="date"
value={dateRange.start}
onChange={(e) => setDateRange((prev) => ({ ...prev, start: e.target.value }))}
@@ -720,8 +720,8 @@ function LogsTab({ logs, workers, tasks, dateRange, setDateRange, selectedWorker
/>
</div>
<div className="space-y-1">
<label className="text-xs text-[var(--admin-text-muted)] font-medium">To</label>
<input aria-label="Date"
<label htmlFor="fld-4-to-2" className="text-xs text-[var(--admin-text-muted)] font-medium">To</label>
<input id="fld-4-to-2" aria-label="Date"
type="date"
value={dateRange.end}
onChange={(e) => setDateRange((prev) => ({ ...prev, end: e.target.value }))}
@@ -729,8 +729,8 @@ function LogsTab({ logs, workers, tasks, dateRange, setDateRange, selectedWorker
/>
</div>
<div className="space-y-1">
<label className="text-xs text-[var(--admin-text-muted)] font-medium">Worker</label>
<select aria-label="Select"
<label htmlFor="fld-5-worker" className="text-xs text-[var(--admin-text-muted)] font-medium">Worker</label>
<select id="fld-5-worker" aria-label="Select"
value={selectedWorker}
onChange={(e) => setSelectedWorker(e.target.value)}
className="px-3 py-2 rounded-lg border border-[var(--admin-border)] bg-white text-[var(--admin-text-primary)] text-sm focus:outline-none focus:border-[var(--admin-accent)] min-w-[160px]"
@@ -742,8 +742,8 @@ function LogsTab({ logs, workers, tasks, dateRange, setDateRange, selectedWorker
</select>
</div>
<div className="space-y-1">
<label className="text-xs text-[var(--admin-text-muted)] font-medium">Task</label>
<select aria-label="Select"
<label htmlFor="fld-6-task" className="text-xs text-[var(--admin-text-muted)] font-medium">Task</label>
<select id="fld-6-task" aria-label="Select"
value={selectedTask}
onChange={(e) => setSelectedTask(e.target.value)}
className="px-3 py-2 rounded-lg border border-[var(--admin-border)] bg-white text-[var(--admin-text-primary)] text-sm focus:outline-none focus:border-[var(--admin-accent)] min-w-[160px]"
@@ -858,20 +858,20 @@ function WorkerModal({ worker, brandId, onClose, onSave }: {
<GlassModal title={isEdit ? "Edit Worker" : "Add Worker"} onClose={onClose}>
<form onSubmit={handleSubmit} className="p-6 space-y-4">
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Name *</label>
<input aria-label="Input" value={name} onChange={(e) => setName(e.target.value)} required className="w-full rounded-lg border border-[var(--admin-border)] px-3 py-2.5 text-sm focus:outline-none focus:border-[var(--admin-accent)]" />
<label htmlFor="fld-7-name" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Name *</label>
<input id="fld-7-name" aria-label="Input" value={name} onChange={(e) => setName(e.target.value)} required className="w-full rounded-lg border border-[var(--admin-border)] px-3 py-2.5 text-sm focus:outline-none focus:border-[var(--admin-accent)]" />
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Role</label>
<select aria-label="Select" value={role} onChange={(e) => setRole(e.target.value)} className="w-full rounded-lg border border-[var(--admin-border)] px-3 py-2.5 text-sm focus:outline-none focus:border-[var(--admin-accent)]">
<label htmlFor="fld-8-role" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Role</label>
<select id="fld-8-role" aria-label="Select" value={role} onChange={(e) => setRole(e.target.value)} className="w-full rounded-lg border border-[var(--admin-border)] px-3 py-2.5 text-sm focus:outline-none focus:border-[var(--admin-accent)]">
<option value="worker">Worker</option>
<option value="supervisor">Supervisor</option>
<option value="admin">Admin</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Language</label>
<select aria-label="Select" value={lang} onChange={(e) => setLang(e.target.value)} className="w-full rounded-lg border border-[var(--admin-border)] px-3 py-2.5 text-sm focus:outline-none focus:border-[var(--admin-accent)]">
<label htmlFor="fld-9-language" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Language</label>
<select id="fld-9-language" aria-label="Select" value={lang} onChange={(e) => setLang(e.target.value)} className="w-full rounded-lg border border-[var(--admin-border)] px-3 py-2.5 text-sm focus:outline-none focus:border-[var(--admin-accent)]">
<option value="en">English</option>
<option value="es">Español</option>
</select>
@@ -943,16 +943,16 @@ function TaskModal({ task, brandId, onClose, onSave }: {
<GlassModal title={isEdit ? "Edit Task" : "Add Task"} onClose={onClose}>
<form onSubmit={handleSubmit} className="p-6 space-y-4">
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Task Name *</label>
<input aria-label="., Harvesting" value={name} onChange={(e) => setName(e.target.value)} required placeholder="e.g., Harvesting" className="w-full rounded-lg border border-[var(--admin-border)] px-3 py-2.5 text-sm focus:outline-none focus:border-[var(--admin-accent)]" />
<label htmlFor="fld-10-task-name" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Task Name *</label>
<input id="fld-10-task-name" aria-label="., Harvesting" value={name} onChange={(e) => setName(e.target.value)} required placeholder="e.g., Harvesting" className="w-full rounded-lg border border-[var(--admin-border)] px-3 py-2.5 text-sm focus:outline-none focus:border-[var(--admin-accent)]" />
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Name in Spanish</label>
<input aria-label="., Cosecha" value={nameEs} onChange={(e) => setNameEs(e.target.value)} placeholder="e.g., Cosecha" className="w-full rounded-lg border border-[var(--admin-border)] px-3 py-2.5 text-sm focus:outline-none focus:border-[var(--admin-accent)]" />
<label htmlFor="fld-11-name-in-spanish" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Name in Spanish</label>
<input id="fld-11-name-in-spanish" aria-label="., Cosecha" value={nameEs} onChange={(e) => setNameEs(e.target.value)} placeholder="e.g., Cosecha" className="w-full rounded-lg border border-[var(--admin-border)] px-3 py-2.5 text-sm focus:outline-none focus:border-[var(--admin-accent)]" />
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Unit</label>
<select aria-label="Select" value={unit} onChange={(e) => setUnit(e.target.value)} className="w-full rounded-lg border border-[var(--admin-border)] px-3 py-2.5 text-sm focus:outline-none focus:border-[var(--admin-accent)]">
<label htmlFor="fld-12-unit" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Unit</label>
<select id="fld-12-unit" aria-label="Select" value={unit} onChange={(e) => setUnit(e.target.value)} className="w-full rounded-lg border border-[var(--admin-border)] px-3 py-2.5 text-sm focus:outline-none focus:border-[var(--admin-accent)]">
<option value="hours">Hours</option>
<option value="pieces">Pieces</option>
<option value="units">Units</option>
@@ -528,8 +528,8 @@ export default function TimeTrackingSettingsClient({ brandId }: TimeTrackingSett
<h3 className="text-sm font-semibold text-[var(--admin-text-primary)]">Pay Period & Overtime</h3>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-[var(--admin-text-muted)] mb-2">Work week starts on</label>
<select aria-label="Select" value={payPeriodStartDay} onChange={e => setPayPeriodStartDay(Number(e.target.value))}
<label htmlFor="fld-1-work-week-starts-on" className="block text-xs font-semibold uppercase tracking-widest text-[var(--admin-text-muted)] mb-2">Work week starts on</label>
<select id="fld-1-work-week-starts-on" aria-label="Select" value={payPeriodStartDay} onChange={e => setPayPeriodStartDay(Number(e.target.value))}
className="w-full px-4 py-3 rounded-xl border border-[var(--admin-border)] bg-white text-[var(--admin-text-primary)] focus:outline-none focus:border-[var(--admin-accent)] transition-colors">
{DAYS.map((d, i) => <option key={d} value={i}>{d}</option>)}
</select>
@@ -685,23 +685,23 @@ export default function TimeTrackingSettingsClient({ brandId }: TimeTrackingSett
</div>
)}
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-[var(--admin-text-muted)] mb-1.5">Name *</label>
<input aria-label="Worker Name" value={workerName} onChange={e => setWorkerName(e.target.value)}
<label htmlFor="fld-2-name" className="block text-xs font-semibold uppercase tracking-widest text-[var(--admin-text-muted)] mb-1.5">Name *</label>
<input id="fld-2-name" aria-label="Worker Name" value={workerName} onChange={e => setWorkerName(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-[var(--admin-border)] bg-white text-[var(--admin-text-primary)] placeholder:text-[var(--admin-text-muted)] focus:outline-none focus:border-[var(--admin-accent)] transition-colors"
placeholder="Worker name" />
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-[var(--admin-text-muted)] mb-1.5">Role</label>
<select aria-label="Select" value={workerRole} onChange={e => setWorkerRole(e.target.value)}
<label htmlFor="fld-3-role" className="block text-xs font-semibold uppercase tracking-widest text-[var(--admin-text-muted)] mb-1.5">Role</label>
<select id="fld-3-role" aria-label="Select" value={workerRole} onChange={e => setWorkerRole(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-[var(--admin-border)] bg-white text-[var(--admin-text-primary)] focus:outline-none focus:border-[var(--admin-accent)] transition-colors">
<option value="worker">Worker</option>
<option value="time_admin">Time Admin</option>
</select>
</div>
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-[var(--admin-text-muted)] mb-1.5">Lang</label>
<select aria-label="Select" value={workerLang} onChange={e => setWorkerLang(e.target.value)}
<label htmlFor="fld-4-lang" className="block text-xs font-semibold uppercase tracking-widest text-[var(--admin-text-muted)] mb-1.5">Lang</label>
<select id="fld-4-lang" aria-label="Select" value={workerLang} onChange={e => setWorkerLang(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-[var(--admin-border)] bg-white text-[var(--admin-text-primary)] focus:outline-none focus:border-[var(--admin-accent)] transition-colors">
<option value="en">English</option>
<option value="es">Español</option>
@@ -744,21 +744,21 @@ export default function TimeTrackingSettingsClient({ brandId }: TimeTrackingSett
<div className="p-6 space-y-4">
{taskError && <div className="bg-red-50 border border-red-200 rounded-xl py-3 px-4 text-red-700 text-sm">{taskError}</div>}
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-[var(--admin-text-muted)] mb-1.5">Name (EN) *</label>
<input aria-label=". Harvesting" value={taskName} onChange={e => setTaskName(e.target.value)}
<label htmlFor="fld-5-name-en" className="block text-xs font-semibold uppercase tracking-widest text-[var(--admin-text-muted)] mb-1.5">Name (EN) *</label>
<input id="fld-5-name-en" aria-label=". Harvesting" value={taskName} onChange={e => setTaskName(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-[var(--admin-border)] bg-white text-[var(--admin-text-primary)] placeholder:text-[var(--admin-text-muted)] focus:outline-none focus:border-[var(--admin-accent)] transition-colors"
placeholder="e.g. Harvesting" />
</div>
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-[var(--admin-text-muted)] mb-1.5">Name (ES)</label>
<input aria-label=". Cosecha" value={taskNameEs} onChange={e => setTaskNameEs(e.target.value)}
<label htmlFor="fld-6-name-es" className="block text-xs font-semibold uppercase tracking-widest text-[var(--admin-text-muted)] mb-1.5">Name (ES)</label>
<input id="fld-6-name-es" aria-label=". Cosecha" value={taskNameEs} onChange={e => setTaskNameEs(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-[var(--admin-border)] bg-white text-[var(--admin-text-primary)] placeholder:text-[var(--admin-text-muted)] focus:outline-none focus:border-[var(--admin-accent)] transition-colors"
placeholder="e.g. Cosecha" />
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-[var(--admin-text-muted)] mb-1.5">Unit</label>
<select aria-label="Select" value={taskUnit} onChange={e => setTaskUnit(e.target.value)}
<label htmlFor="fld-7-unit" className="block text-xs font-semibold uppercase tracking-widest text-[var(--admin-text-muted)] mb-1.5">Unit</label>
<select id="fld-7-unit" aria-label="Select" value={taskUnit} onChange={e => setTaskUnit(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-[var(--admin-border)] bg-white text-[var(--admin-text-primary)] focus:outline-none focus:border-[var(--admin-accent)] transition-colors">
<option value="hours">Hours</option>
<option value="pieces">Pieces</option>
@@ -766,8 +766,8 @@ export default function TimeTrackingSettingsClient({ brandId }: TimeTrackingSett
</select>
</div>
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-[var(--admin-text-muted)] mb-1.5">Sort Order</label>
<input aria-label="Number" type="number" value={taskSortOrder} onChange={e => setTaskSortOrder(parseInt(e.target.value) || 0)}
<label htmlFor="fld-8-sort-order" className="block text-xs font-semibold uppercase tracking-widest text-[var(--admin-text-muted)] mb-1.5">Sort Order</label>
<input id="fld-8-sort-order" aria-label="Number" type="number" value={taskSortOrder} onChange={e => setTaskSortOrder(parseInt(e.target.value) || 0)}
className="w-full px-4 py-3 rounded-xl border border-[var(--admin-border)] bg-white text-[var(--admin-text-primary)] focus:outline-none focus:border-[var(--admin-accent)] transition-colors" />
</div>
</div>
+4 -4
View File
@@ -539,8 +539,8 @@ export default function UsersPage({ initialUsers, brands, currentUser, onUserCre
{/* Email (create only) */}
{editing.isNew && (
<div>
<label className="block text-sm font-medium text-stone-700">Email</label>
<input aria-label="User@example.com"
<label htmlFor="fld-1-email" className="block text-sm font-medium text-stone-700">Email</label>
<input id="fld-1-email" aria-label="User@example.com"
type="email"
value={editing.email ?? ""}
onChange={(e) => setEditing((p) => ({ ...p, email: e.target.value }))}
@@ -622,8 +622,8 @@ export default function UsersPage({ initialUsers, brands, currentUser, onUserCre
{/* Brand */}
{showBrandSelect && (
<div>
<label className="block text-sm font-medium text-stone-700">Brand</label>
<select aria-label="Select"
<label htmlFor="fld-2-brand" className="block text-sm font-medium text-stone-700">Brand</label>
<select id="fld-2-brand" aria-label="Select"
value={editing.brand_id ?? ""}
onChange={(e) => setBrand(e.target.value || null)}
className="mt-1 w-full rounded-lg border border-stone-200 px-3 py-2.5 text-sm text-stone-900 focus:border-emerald-500 focus:outline-none focus:ring-1 focus:ring-emerald-500"
+8 -8
View File
@@ -101,8 +101,8 @@ export default function WaterUserEditForm({ waterUser, backHref = "/admin/water-
<div className="grid grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium text-[var(--admin-text-muted)] mb-1">Name</label>
<input aria-label="Text"
<label htmlFor="fld-1-name" className="block text-sm font-medium text-[var(--admin-text-muted)] mb-1">Name</label>
<input id="fld-1-name" aria-label="Text"
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
@@ -111,8 +111,8 @@ export default function WaterUserEditForm({ waterUser, backHref = "/admin/water-
/>
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-muted)] mb-1">Role</label>
<select aria-label="Select"
<label htmlFor="fld-2-role" className="block text-sm font-medium text-[var(--admin-text-muted)] mb-1">Role</label>
<select id="fld-2-role" aria-label="Select"
value={role}
onChange={(e) => setRole(e.target.value as "irrigator" | "water_admin")}
className="w-full rounded-lg border border-[var(--admin-border)] px-3 py-2 text-sm"
@@ -127,8 +127,8 @@ export default function WaterUserEditForm({ waterUser, backHref = "/admin/water-
</p>
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-muted)] mb-1">Language</label>
<select aria-label="Select"
<label htmlFor="fld-3-language" className="block text-sm font-medium text-[var(--admin-text-muted)] mb-1">Language</label>
<select id="fld-3-language" aria-label="Select"
value={lang}
onChange={(e) => setLang(e.target.value)}
className="w-full rounded-lg border border-[var(--admin-border)] px-3 py-2 text-sm"
@@ -141,8 +141,8 @@ export default function WaterUserEditForm({ waterUser, backHref = "/admin/water-
<div className="flex items-center gap-4">
<div>
<label className="block text-sm font-medium text-[var(--admin-text-muted)] mb-1">Status</label>
<select aria-label="Select"
<label htmlFor="fld-4-status" className="block text-sm font-medium text-[var(--admin-text-muted)] mb-1">Status</label>
<select id="fld-4-status" aria-label="Select"
value={active ? "1" : "0"}
onChange={(e) => setActive(e.target.value === "1")}
className="rounded-lg border border-[var(--admin-border)] px-3 py-2 text-sm"
@@ -320,10 +320,8 @@ export default function FsmaReportModal({ brandId }: { brandId: string }) {
{/* Date Range Selector */}
<div className="flex items-end gap-4 mb-6">
<div className="flex-1">
<label className="block text-xs font-semibold text-stone-500 mb-1.5 uppercase tracking-wider">
Start Date
</label>
<input aria-label="Date"
<label htmlFor="fld-1-start-date" className="block text-xs font-semibold text-stone-500 mb-1.5 uppercase tracking-wider">Start Date</label>
<input id="fld-1-start-date" aria-label="Date"
type="date"
value={startDate}
onChange={(e) => setStartDate(e.target.value)}
@@ -331,10 +329,8 @@ export default function FsmaReportModal({ brandId }: { brandId: string }) {
/>
</div>
<div className="flex-1">
<label className="block text-xs font-semibold text-stone-500 mb-1.5 uppercase tracking-wider">
End Date
</label>
<input aria-label="Date"
<label htmlFor="fld-2-end-date" className="block text-xs font-semibold text-stone-500 mb-1.5 uppercase tracking-wider">End Date</label>
<input id="fld-2-end-date" aria-label="Date"
type="date"
value={endDate}
onChange={(e) => setEndDate(e.target.value)}
+22 -22
View File
@@ -94,8 +94,8 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
{/* Required — large touch targets for field use */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-semibold text-stone-800 mb-2">Crop Type *</label>
<input aria-label=". Sweet Corn"
<label htmlFor="fld-1-crop-type" className="block text-sm font-semibold text-stone-800 mb-2">Crop Type *</label>
<input id="fld-1-crop-type" aria-label=". Sweet Corn"
type="text"
value={crop_type}
onChange={(e) => setCropType(e.target.value)}
@@ -105,8 +105,8 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
/>
</div>
<div>
<label className="block text-sm font-semibold text-stone-800 mb-2">Variety</label>
<input aria-label=". Golden Bantam"
<label htmlFor="fld-2-variety" className="block text-sm font-semibold text-stone-800 mb-2">Variety</label>
<input id="fld-2-variety" aria-label=". Golden Bantam"
type="text"
value={variety}
onChange={(e) => setVariety(e.target.value)}
@@ -117,8 +117,8 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
</div>
<div>
<label className="block text-sm font-semibold text-stone-800 mb-2">Harvest Date *</label>
<input aria-label="Date"
<label htmlFor="fld-3-harvest-date" className="block text-sm font-semibold text-stone-800 mb-2">Harvest Date *</label>
<input id="fld-3-harvest-date" aria-label="Date"
type="date"
value={harvest_date}
onChange={(e) => setHarvestDate(e.target.value)}
@@ -132,8 +132,8 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
<p className="text-xs font-semibold uppercase tracking-wider text-stone-400 mb-3">Field &amp; Location</p>
<div className="space-y-3">
<div>
<label className="block text-xs font-medium text-stone-600 mb-1.5">Field / Location *</label>
<input aria-label=". North Field"
<label htmlFor="fld-4-field-location" className="block text-xs font-medium text-stone-600 mb-1.5">Field / Location *</label>
<input id="fld-4-field-location" aria-label=". North Field"
type="text"
value={field_location}
onChange={(e) => setFieldLocation(e.target.value)}
@@ -144,8 +144,8 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-medium text-stone-600 mb-1.5">Field Block</label>
<input aria-label=". Block A"
<label htmlFor="fld-5-field-block" className="block text-xs font-medium text-stone-600 mb-1.5">Field Block</label>
<input id="fld-5-field-block" aria-label=". Block A"
type="text"
value={field_block}
onChange={(e) => setFieldBlock(e.target.value)}
@@ -154,8 +154,8 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
/>
</div>
<div>
<label className="block text-xs font-medium text-stone-600 mb-1.5">Worker / Harvest Lead</label>
<input aria-label=". Maria S."
<label htmlFor="fld-6-worker-harvest-lead" className="block text-xs font-medium text-stone-600 mb-1.5">Worker / Harvest Lead</label>
<input id="fld-6-worker-harvest-lead" aria-label=". Maria S."
type="text"
value={worker_name}
onChange={(e) => setWorkerName(e.target.value)}
@@ -172,8 +172,8 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
<p className="text-xs font-semibold uppercase tracking-wider text-stone-400 mb-3">Weight &amp; Yield</p>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-medium text-stone-600 mb-1.5">Actual Qty</label>
<input aria-label=". 4800"
<label htmlFor="fld-7-actual-qty" className="block text-xs font-medium text-stone-600 mb-1.5">Actual Qty</label>
<input id="fld-7-actual-qty" aria-label=". 4800"
type="number"
value={quantity_lbs}
onChange={(e) => setQuantityLbs(e.target.value)}
@@ -228,8 +228,8 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
<p className="text-xs font-semibold uppercase tracking-wider text-stone-400 mb-3">Bin, Container &amp; Pallets</p>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-medium text-stone-600 mb-1.5">Bin ID</label>
<input aria-label=". BIN 001"
<label htmlFor="fld-8-bin-id" className="block text-xs font-medium text-stone-600 mb-1.5">Bin ID</label>
<input id="fld-8-bin-id" aria-label=". BIN 001"
type="text"
value={bin_id}
onChange={(e) => setBinId(e.target.value)}
@@ -238,8 +238,8 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
/>
</div>
<div>
<label className="block text-xs font-medium text-stone-600 mb-1.5">Container ID</label>
<input aria-label=". CONT A42"
<label htmlFor="fld-9-container-id" className="block text-xs font-medium text-stone-600 mb-1.5">Container ID</label>
<input id="fld-9-container-id" aria-label=". CONT A42"
type="text"
value={container_id}
onChange={(e) => setContainerId(e.target.value)}
@@ -250,8 +250,8 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
</div>
<div className="mt-3 grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-medium text-stone-600 mb-1.5">Pallets</label>
<input aria-label=". 4"
<label htmlFor="fld-10-pallets" className="block text-xs font-medium text-stone-600 mb-1.5">Pallets</label>
<input id="fld-10-pallets" aria-label=". 4"
type="number"
value={pallets}
onChange={(e) => setPallets(e.target.value)}
@@ -261,8 +261,8 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
/>
</div>
<div>
<label className="block text-xs font-medium text-stone-600 mb-1.5">Packer Name</label>
<input aria-label=". Jose R."
<label htmlFor="fld-11-packer-name" className="block text-xs font-medium text-stone-600 mb-1.5">Packer Name</label>
<input id="fld-11-packer-name" aria-label=". Jose R."
type="text"
value={packer_name}
onChange={(e) => setPackerName(e.target.value)}
+22 -22
View File
@@ -150,8 +150,8 @@ export default function LotCreateModal({ isOpen, onClose, brandId }: Props) {
{/* Required fields */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-semibold mb-2" style={{ color: "var(--admin-text-primary)" }}>Crop Type *</label>
<input aria-label=". Sweet Corn"
<label htmlFor="fld-1-crop-type" className="block text-sm font-semibold mb-2" style={{ color: "var(--admin-text-primary)" }}>Crop Type *</label>
<input id="fld-1-crop-type" aria-label=". Sweet Corn"
type="text"
value={crop_type}
onChange={(e) => setCropType(e.target.value)}
@@ -166,8 +166,8 @@ export default function LotCreateModal({ isOpen, onClose, brandId }: Props) {
/>
</div>
<div>
<label className="block text-sm font-semibold mb-2" style={{ color: "var(--admin-text-primary)" }}>Variety</label>
<input aria-label=". Golden Bantam"
<label htmlFor="fld-2-variety" className="block text-sm font-semibold mb-2" style={{ color: "var(--admin-text-primary)" }}>Variety</label>
<input id="fld-2-variety" aria-label=". Golden Bantam"
type="text"
value={variety}
onChange={(e) => setVariety(e.target.value)}
@@ -183,8 +183,8 @@ export default function LotCreateModal({ isOpen, onClose, brandId }: Props) {
</div>
<div>
<label className="block text-sm font-semibold mb-2" style={{ color: "var(--admin-text-primary)" }}>Harvest Date *</label>
<input aria-label="Date"
<label htmlFor="fld-3-harvest-date" className="block text-sm font-semibold mb-2" style={{ color: "var(--admin-text-primary)" }}>Harvest Date *</label>
<input id="fld-3-harvest-date" aria-label="Date"
type="date"
value={harvest_date}
onChange={(e) => setHarvestDate(e.target.value)}
@@ -203,8 +203,8 @@ export default function LotCreateModal({ isOpen, onClose, brandId }: Props) {
<p className="text-xs font-semibold uppercase tracking-wider mb-3" style={{ color: "var(--admin-text-muted)" }}>Field & Location</p>
<div className="space-y-3">
<div>
<label className="block text-xs font-medium mb-1.5" style={{ color: "var(--admin-text-primary)" }}>Field / Location *</label>
<input aria-label=". North Field"
<label htmlFor="fld-4-field-location" className="block text-xs font-medium mb-1.5" style={{ color: "var(--admin-text-primary)" }}>Field / Location *</label>
<input id="fld-4-field-location" aria-label=". North Field"
type="text"
value={field_location}
onChange={(e) => setFieldLocation(e.target.value)}
@@ -220,8 +220,8 @@ export default function LotCreateModal({ isOpen, onClose, brandId }: Props) {
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-medium mb-1.5" style={{ color: "var(--admin-text-primary)" }}>Field Block</label>
<input aria-label=". Block A"
<label htmlFor="fld-5-field-block" className="block text-xs font-medium mb-1.5" style={{ color: "var(--admin-text-primary)" }}>Field Block</label>
<input id="fld-5-field-block" aria-label=". Block A"
type="text"
value={field_block}
onChange={(e) => setFieldBlock(e.target.value)}
@@ -235,8 +235,8 @@ export default function LotCreateModal({ isOpen, onClose, brandId }: Props) {
/>
</div>
<div>
<label className="block text-xs font-medium mb-1.5" style={{ color: "var(--admin-text-primary)" }}>Worker / Harvest Lead</label>
<input aria-label=". Maria S."
<label htmlFor="fld-6-worker-harvest-lead" className="block text-xs font-medium mb-1.5" style={{ color: "var(--admin-text-primary)" }}>Worker / Harvest Lead</label>
<input id="fld-6-worker-harvest-lead" aria-label=". Maria S."
type="text"
value={worker_name}
onChange={(e) => setWorkerName(e.target.value)}
@@ -258,8 +258,8 @@ export default function LotCreateModal({ isOpen, onClose, brandId }: Props) {
<p className="text-xs font-semibold uppercase tracking-wider mb-3" style={{ color: "var(--admin-text-muted)" }}>Weight & Yield</p>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-medium mb-1.5" style={{ color: "var(--admin-text-primary)" }}>Actual Qty</label>
<input aria-label=". 4800"
<label htmlFor="fld-7-actual-qty" className="block text-xs font-medium mb-1.5" style={{ color: "var(--admin-text-primary)" }}>Actual Qty</label>
<input id="fld-7-actual-qty" aria-label=". 4800"
type="number"
value={quantity_lbs}
onChange={(e) => setQuantityLbs(e.target.value)}
@@ -330,8 +330,8 @@ export default function LotCreateModal({ isOpen, onClose, brandId }: Props) {
<p className="text-xs font-semibold uppercase tracking-wider mb-3" style={{ color: "var(--admin-text-muted)" }}>Bin, Container & Pallets</p>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-medium mb-1.5" style={{ color: "var(--admin-text-primary)" }}>Bin ID</label>
<input aria-label=". BIN 001"
<label htmlFor="fld-8-bin-id" className="block text-xs font-medium mb-1.5" style={{ color: "var(--admin-text-primary)" }}>Bin ID</label>
<input id="fld-8-bin-id" aria-label=". BIN 001"
type="text"
value={bin_id}
onChange={(e) => setBinId(e.target.value)}
@@ -345,8 +345,8 @@ export default function LotCreateModal({ isOpen, onClose, brandId }: Props) {
/>
</div>
<div>
<label className="block text-xs font-medium mb-1.5" style={{ color: "var(--admin-text-primary)" }}>Container ID</label>
<input aria-label=". CONT A42"
<label htmlFor="fld-9-container-id" className="block text-xs font-medium mb-1.5" style={{ color: "var(--admin-text-primary)" }}>Container ID</label>
<input id="fld-9-container-id" aria-label=". CONT A42"
type="text"
value={container_id}
onChange={(e) => setContainerId(e.target.value)}
@@ -362,8 +362,8 @@ export default function LotCreateModal({ isOpen, onClose, brandId }: Props) {
</div>
<div className="mt-3 grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-medium mb-1.5" style={{ color: "var(--admin-text-primary)" }}>Pallets</label>
<input aria-label=". 4"
<label htmlFor="fld-10-pallets" className="block text-xs font-medium mb-1.5" style={{ color: "var(--admin-text-primary)" }}>Pallets</label>
<input id="fld-10-pallets" aria-label=". 4"
type="number"
value={pallets}
onChange={(e) => setPallets(e.target.value)}
@@ -378,8 +378,8 @@ export default function LotCreateModal({ isOpen, onClose, brandId }: Props) {
/>
</div>
<div>
<label className="block text-xs font-medium mb-1.5" style={{ color: "var(--admin-text-primary)" }}>Packer Name</label>
<input aria-label=". Jose R."
<label htmlFor="fld-11-packer-name" className="block text-xs font-medium mb-1.5" style={{ color: "var(--admin-text-primary)" }}>Packer Name</label>
<input id="fld-11-packer-name" aria-label=". Jose R."
type="text"
value={packer_name}
onChange={(e) => setPackerName(e.target.value)}
+16 -16
View File
@@ -750,8 +750,8 @@ export default function LotDetailPanel({
</div>
<div className="space-y-4 p-6">
<div>
<label className="block text-sm font-semibold text-[var(--admin-text-secondary)] mb-1.5">New Status</label>
<select aria-label="Select"
<label htmlFor="fld-1-new-status" className="block text-sm font-semibold text-[var(--admin-text-secondary)] mb-1.5">New Status</label>
<select id="fld-1-new-status" aria-label="Select"
value={newStatus}
onChange={(e) => setNewStatus(e.target.value)}
required
@@ -769,8 +769,8 @@ export default function LotDetailPanel({
</select>
</div>
<div>
<label className="block text-sm font-semibold text-[var(--admin-text-secondary)] mb-1.5">Location</label>
<input aria-label=". Warehouse A"
<label htmlFor="fld-2-location" className="block text-sm font-semibold text-[var(--admin-text-secondary)] mb-1.5">Location</label>
<input id="fld-2-location" aria-label=". Warehouse A"
type="text"
value={location}
onChange={(e) => setLocation(e.target.value)}
@@ -779,8 +779,8 @@ export default function LotDetailPanel({
/>
</div>
<div>
<label className="block text-sm font-semibold text-[var(--admin-text-secondary)] mb-1.5">Notes</label>
<textarea aria-label="Any Notes..."
<label htmlFor="fld-3-notes" className="block text-sm font-semibold text-[var(--admin-text-secondary)] mb-1.5">Notes</label>
<textarea id="fld-3-notes" aria-label="Any Notes..."
value={notes}
onChange={(e) => setNotes(e.target.value)}
placeholder="Any notes..."
@@ -820,8 +820,8 @@ export default function LotDetailPanel({
</div>
<div className="space-y-4 p-6">
<div>
<label className="block text-sm font-semibold text-[var(--admin-text-secondary)] mb-1.5">Bin or Container ID *</label>
<input aria-label=". BIN 042"
<label htmlFor="fld-4-bin-or-container-id" className="block text-sm font-semibold text-[var(--admin-text-secondary)] mb-1.5">Bin or Container ID *</label>
<input id="fld-4-bin-or-container-id" aria-label=". BIN 042"
type="text"
value={binId}
onChange={(e) => setBinId(e.target.value)}
@@ -831,8 +831,8 @@ export default function LotDetailPanel({
/>
</div>
<div>
<label className="block text-sm font-semibold text-[var(--admin-text-secondary)] mb-1.5">Notes</label>
<textarea aria-label="Weight, Count, Or Other Details..."
<label htmlFor="fld-5-notes-2" className="block text-sm font-semibold text-[var(--admin-text-secondary)] mb-1.5">Notes</label>
<textarea id="fld-5-notes-2" aria-label="Weight, Count, Or Other Details..."
value={notes}
onChange={(e) => setNotes(e.target.value)}
placeholder="Weight, count, or other details..."
@@ -874,8 +874,8 @@ export default function LotDetailPanel({
</div>
<div className="space-y-4 p-6">
<div>
<label className="block text-sm font-semibold text-[var(--admin-text-secondary)] mb-1.5">Order ID *</label>
<input aria-label=". 4f3e2a1b ..."
<label htmlFor="fld-6-order-id" className="block text-sm font-semibold text-[var(--admin-text-secondary)] mb-1.5">Order ID *</label>
<input id="fld-6-order-id" aria-label=". 4f3e2a1b ..."
type="text"
value={usedOrderId}
onChange={(e) => setUsedOrderId(e.target.value.toUpperCase())}
@@ -885,8 +885,8 @@ export default function LotDetailPanel({
/>
</div>
<div>
<label className="block text-sm font-semibold text-[var(--admin-text-secondary)] mb-1.5">Quantity Used (lbs)</label>
<input aria-label="Leave Blank To Auto Detect"
<label htmlFor="fld-7-quantity-used-lbs" className="block text-sm font-semibold text-[var(--admin-text-secondary)] mb-1.5">Quantity Used (lbs)</label>
<input id="fld-7-quantity-used-lbs" aria-label="Leave Blank To Auto Detect"
type="number"
value={usedQty}
onChange={(e) => setUsedQty(e.target.value)}
@@ -896,8 +896,8 @@ export default function LotDetailPanel({
/>
</div>
<div>
<label className="block text-sm font-semibold text-[var(--admin-text-secondary)] mb-1.5">Notes</label>
<textarea aria-label="Any Notes..."
<label htmlFor="fld-8-notes-3" className="block text-sm font-semibold text-[var(--admin-text-secondary)] mb-1.5">Notes</label>
<textarea id="fld-8-notes-3" aria-label="Any Notes..."
value={usedNotes}
onChange={(e) => setUsedNotes(e.target.value)}
placeholder="Any notes..."
+2 -2
View File
@@ -282,8 +282,8 @@ export default function QRScanModal({ onClose, onScanResult }: QRScanModalProps)
) : (
<form onSubmit={handleManualSubmit} className="space-y-4">
<div>
<label className="block text-sm font-semibold text-stone-700 mb-1.5">Lot Number</label>
<input aria-label=". TC 20260520 001"
<label htmlFor="fld-1-lot-number" className="block text-sm font-semibold text-stone-700 mb-1.5">Lot Number</label>
<input id="fld-1-lot-number" aria-label=". TC 20260520 001"
type="text"
value={manualInput}
onChange={(e) => setManualInput(e.target.value.toUpperCase())}
+10 -20
View File
@@ -94,10 +94,8 @@ export default function QuickNewLotModal({ brandId, onCreated, onClose }: Props)
</div>
<div>
<label className="block text-sm font-medium text-stone-700 mb-1.5">
Harvest Date
</label>
<input aria-label="Date"
<label htmlFor="fld-1-harvest-date" className="block text-sm font-medium text-stone-700 mb-1.5">Harvest Date</label>
<input id="fld-1-harvest-date" aria-label="Date"
type="date"
value={harvest_date}
onChange={(e) => setHarvestDate(e.target.value)}
@@ -106,10 +104,8 @@ export default function QuickNewLotModal({ brandId, onCreated, onClose }: Props)
</div>
<div>
<label className="block text-sm font-medium text-stone-700 mb-1.5">
Field / Location
</label>
<input aria-label=". North Field"
<label htmlFor="fld-2-field-location" className="block text-sm font-medium text-stone-700 mb-1.5">Field / Location</label>
<input id="fld-2-field-location" aria-label=". North Field"
type="text"
value={field_location}
onChange={(e) => setFieldLocation(e.target.value)}
@@ -120,10 +116,8 @@ export default function QuickNewLotModal({ brandId, onCreated, onClose }: Props)
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-sm font-medium text-stone-700 mb-1.5">
Worker
</label>
<input aria-label="Name"
<label htmlFor="fld-3-worker" className="block text-sm font-medium text-stone-700 mb-1.5">Worker</label>
<input id="fld-3-worker" aria-label="Name"
type="text"
value={worker_name}
onChange={(e) => setWorkerName(e.target.value)}
@@ -132,10 +126,8 @@ export default function QuickNewLotModal({ brandId, onCreated, onClose }: Props)
/>
</div>
<div>
<label className="block text-sm font-medium text-stone-700 mb-1.5">
Variety
</label>
<input aria-label="Type"
<label htmlFor="fld-4-variety" className="block text-sm font-medium text-stone-700 mb-1.5">Variety</label>
<input id="fld-4-variety" aria-label="Type"
type="text"
value={variety}
onChange={(e) => setVariety(e.target.value)}
@@ -146,10 +138,8 @@ export default function QuickNewLotModal({ brandId, onCreated, onClose }: Props)
</div>
<div>
<label className="block text-sm font-medium text-stone-700 mb-1.5">
Quantity (lbs)
</label>
<input aria-label="0"
<label htmlFor="fld-5-quantity-lbs" className="block text-sm font-medium text-stone-700 mb-1.5">Quantity (lbs)</label>
<input id="fld-5-quantity-lbs" aria-label="0"
type="number"
value={quantity_lbs}
onChange={(e) => setQuantityLbs(e.target.value)}
@@ -65,8 +65,8 @@ export default function RouteTraceSettings() {
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-stone-700 mb-1.5">Prefix</label>
<input aria-label="TC"
<label htmlFor="fld-1-prefix" className="block text-sm font-medium text-stone-700 mb-1.5">Prefix</label>
<input id="fld-1-prefix" aria-label="TC"
type="text"
value={lotPrefix}
onChange={(e) => setLotPrefix(e.target.value.toUpperCase())}
@@ -91,8 +91,8 @@ export default function RouteTraceSettings() {
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-stone-700 mb-1.5">Default Harvest Days Ago</label>
<input aria-label="Number"
<label htmlFor="fld-2-default-harvest-days-ago" className="block text-sm font-medium text-stone-700 mb-1.5">Default Harvest Days Ago</label>
<input id="fld-2-default-harvest-days-ago" aria-label="Number"
type="number"
value={defaultHarvestDays}
onChange={(e) => setDefaultHarvestDays(e.target.value)}
@@ -117,13 +117,12 @@ export default function RouteTraceSettings() {
</div>
</div>
<div>
<label className="block text-sm font-medium text-stone-700 mb-1.5">Base URL</label>
<input aria-label="Https://yourdomain.com"
<label htmlFor="fld-3-base-url" className="block text-sm font-medium text-stone-700 mb-1.5">Base URL</label>
<input id="fld-3-base-url" aria-label="Https://yourdomain.com"
type="text"
defaultValue={typeof window !== "undefined" ? window.location.origin : ""}
placeholder="https://yourdomain.com"
className="w-full rounded-lg border border-stone-200 bg-stone-50 px-3 py-2.5 text-sm outline-none focus:border-emerald-600 focus:bg-white transition-colors"
/>
className="w-full rounded-lg border border-stone-200 bg-stone-50 px-3 py-2.5 text-sm outline-none focus:border-emerald-600 focus:bg-white transition-colors"/>
<p className="text-xs text-stone-400 mt-1.5">QR codes will link to: baseurl/trace/LOTNUMBER</p>
</div>
</div>
@@ -70,6 +70,7 @@ function MorphingProductCard({
src={product.imageUrl}
alt={product.name}
fill
sizes="100vw"
className="object-cover"
/>
)}
@@ -203,6 +203,7 @@ export default function ProductCard({
src={imageUrl}
alt={name}
fill
sizes="(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 25vw"
style={{ objectFit: "cover" }}
className={`transition-transform duration-500 ease-out group-hover:scale-105 ${isLoading ? "opacity-0" : "opacity-100"}`}
onLoad={() => setIsLoading(false)}
@@ -88,11 +88,11 @@ export default function StorefrontFooter({
<div className="md:col-span-1">
{logoUrlDark ? (
<span className="relative inline-block h-8 w-[140px] mb-5">
<Image src={logoUrlDark} alt={brandName} fill style={{ objectFit: "contain", objectPosition: "left" }} />
<Image src={logoUrlDark} alt={brandName} fill sizes="160px" style={{ objectFit: "contain", objectPosition: "left" }} />
</span>
) : logoUrl ? (
<span className="relative inline-block h-8 w-[140px] mb-5">
<Image src={logoUrl} alt={brandName} fill style={{ objectFit: "contain", objectPosition: "left" }} />
<Image src={logoUrl} alt={brandName} fill sizes="160px" style={{ objectFit: "contain", objectPosition: "left" }} />
</span>
) : (
<p className="text-lg font-bold text-stone-950 mb-3">{brandName}</p>
@@ -78,11 +78,11 @@ export default function StorefrontHeader({
<Link href={`/${brandSlug}`} className="flex items-center gap-3 group">
{logoUrlDark ? (
<span className="relative h-9 sm:h-10 w-[140px] sm:w-[160px]">
<Image src={logoUrlDark} alt={brandName} fill className="object-contain object-left transition-transform duration-300 group-hover:scale-[1.02]" />
<Image src={logoUrlDark} alt={brandName} fill sizes="160px" className="object-contain object-left transition-transform duration-300 group-hover:scale-[1.02]" />
</span>
) : logoUrl ? (
<span className="relative h-9 sm:h-10 w-[140px] sm:w-[160px]">
<Image src={logoUrl} alt={brandName} fill className="object-contain object-left transition-transform duration-300 group-hover:scale-[1.02]" />
<Image src={logoUrl} alt={brandName} fill sizes="160px" className="object-contain object-left transition-transform duration-300 group-hover:scale-[1.02]" />
</span>
) : (
<span className="text-lg sm:text-xl font-bold tracking-tight text-stone-800 group-hover:text-stone-900 transition-colors">
@@ -239,6 +239,7 @@ export default function TuxedoVideoHero({
src={logoSrc}
alt="Olathe Sweet"
fill
sizes="(max-width: 1024px) 80vw, 50vw"
style={{ objectFit: "contain" }}
className="drop-shadow-2xl"
priority
@@ -392,7 +392,7 @@ export default function TimeTrackingFieldClient({
<div className="bg-zinc-900 border-b border-zinc-800 px-4 py-3 flex items-center justify-between">
<div className="flex items-center gap-3">
<span className="relative inline-block h-8 w-auto">
{logoUrl ? <Image src={logoUrl} alt="Olathe Sweet" fill style={{ objectFit: "contain" }} className="opacity-80" /> : null}
{logoUrl ? <Image src={logoUrl} alt="Olathe Sweet" fill sizes="80px" style={{ objectFit: "contain" }} className="opacity-80" /> : null}
</span>
<span className="text-sm font-semibold text-zinc-400">{brandName}</span>
</div>
@@ -658,7 +658,7 @@ function TaskSelectScreen({
<div className="space-y-5">
<div className="text-center">
<span className="relative inline-block h-14 w-auto">
{logoUrl ? <Image src={logoUrl} alt="Olathe Sweet" fill style={{ objectFit: "contain" }} className="mx-auto opacity-80 mb-3" /> : null}
{logoUrl ? <Image src={logoUrl} alt="Olathe Sweet" fill sizes="120px" style={{ objectFit: "contain" }} className="mx-auto opacity-80 mb-3" /> : null}
</span>
<h1 className="text-2xl font-black text-white">{t.title}</h1>
<p className="text-sm text-zinc-400 mt-1">{t.select_task}</p>
@@ -914,7 +914,7 @@ function PinEntry({
<div className="space-y-8">
<div className="text-center">
<span className="relative inline-block h-14 w-auto">
{logoUrl ? <Image src={logoUrl} alt="Olathe Sweet" fill style={{ objectFit: "contain" }} className="mx-auto opacity-80 mb-3" /> : null}
{logoUrl ? <Image src={logoUrl} alt="Olathe Sweet" fill sizes="120px" style={{ objectFit: "contain" }} className="mx-auto opacity-80 mb-3" /> : null}
</span>
<h1 className="text-2xl font-black text-white">{t.title}</h1>
</div>
+7 -13
View File
@@ -602,10 +602,8 @@ function WaterFieldInner() {
{/* Measurement + Unit row */}
<div className="flex gap-3">
<div className="flex-1">
<label className="block text-base font-semibold text-stone-700 mb-2">
{t.measurement}
</label>
<input aria-label="0.00"
<label htmlFor="fld-1-tmeasurement" className="block text-base font-semibold text-stone-700 mb-2">{t.measurement}</label>
<input id="fld-1-tmeasurement" aria-label="0.00"
type="number"
step="any"
value={measurement}
@@ -617,10 +615,8 @@ function WaterFieldInner() {
/>
</div>
<div className="w-32">
<label className="block text-base font-semibold text-stone-700 mb-2">
{t.unit}
</label>
<select aria-label="Select"
<label htmlFor="fld-2-tunit" className="block text-base font-semibold text-stone-700 mb-2">{t.unit}</label>
<select id="fld-2-tunit" aria-label="Select"
value={unit}
onChange={(e) => setUnit(e.target.value)}
className="w-full rounded-xl border-2 border-stone-300 bg-white px-4 py-4 text-lg font-semibold outline-none focus:border-stone-900 min-h-[60px]"
@@ -668,7 +664,7 @@ function WaterFieldInner() {
</label>
{photoPreview ? (
<div className="relative rounded-xl overflow-hidden border-2 border-stone-300">
<Image src={photoPreview} alt="Preview" fill style={{ objectFit: "cover" }} />
<Image src={photoPreview} alt="Preview" fill sizes="(max-width: 640px) 100vw, 480px" style={{ objectFit: "cover" }} />
<button
type="button"
onClick={removePhoto}
@@ -692,10 +688,8 @@ function WaterFieldInner() {
{/* Notes */}
<div>
<label className="block text-base font-semibold text-stone-700 mb-2">
{t.notes}
</label>
<textarea aria-label="Textarea"
<label htmlFor="fld-3-tnotes" className="block text-base font-semibold text-stone-700 mb-2">{t.notes}</label>
<textarea id="fld-3-tnotes" aria-label="Textarea"
value={notes}
onChange={(e) => setNotes(e.target.value)}
placeholder={t.notesPlaceholder}
+4 -4
View File
@@ -55,8 +55,8 @@ export default function DepositModal({ order, onClose, onFulfilled }: Props) {
)}
<div className="space-y-3">
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Amount ($)</label>
<input aria-label="Number"
<label htmlFor="fld-1-amount" className="block text-sm font-medium text-slate-700 mb-1">Amount ($)</label>
<input id="fld-1-amount" aria-label="Number"
type="number"
value={amount}
onChange={e => setAmount(e.target.value)}
@@ -67,8 +67,8 @@ export default function DepositModal({ order, onClose, onFulfilled }: Props) {
/>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Method</label>
<select aria-label="Select"
<label htmlFor="fld-2-method" className="block text-sm font-medium text-slate-700 mb-1">Method</label>
<select id="fld-2-method" aria-label="Select"
value={method}
onChange={e => setMethod(e.target.value)}
className="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm outline-none focus:border-slate-900"
+17 -19
View File
@@ -210,8 +210,8 @@ export default function OrdersTab({ orders, customers, brandId, onMsg, onRefresh
{/* Filter bar */}
<div className="flex flex-wrap items-center gap-3 rounded-2xl bg-white border border-[var(--admin-border)] p-4 shadow-sm">
<div>
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1">Status</label>
<select value={statusFilter} onChange={e => setStatusFilter(e.target.value)}
<label htmlFor="fld-1-status" className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1">Status</label>
<select id="fld-1-status" value={statusFilter} onChange={e => setStatusFilter(e.target.value)}
className="rounded-lg border border-[var(--admin-border)] px-3 py-1.5 text-sm outline-none focus:border-[var(--admin-accent)]">
<option value="all">All Statuses</option>
<option value="pending">Pending</option>
@@ -221,13 +221,13 @@ export default function OrdersTab({ orders, customers, brandId, onMsg, onRefresh
</select>
</div>
<div>
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1">Pickup From</label>
<input type="date" value={dateFrom} onChange={e => setDateFrom(e.target.value)}
<label htmlFor="fld-2-pickup-from" className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1">Pickup From</label>
<input id="fld-2-pickup-from" type="date" value={dateFrom} onChange={e => setDateFrom(e.target.value)}
className="rounded-lg border border-[var(--admin-border)] px-3 py-1.5 text-sm outline-none focus:border-[var(--admin-accent)]" />
</div>
<div>
<label className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1">Pickup To</label>
<input type="date" value={dateTo} onChange={e => setDateTo(e.target.value)}
<label htmlFor="fld-3-pickup-to" className="block text-xs font-medium text-[var(--admin-text-muted)] mb-1">Pickup To</label>
<input id="fld-3-pickup-to" type="date" value={dateTo} onChange={e => setDateTo(e.target.value)}
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]">
@@ -359,13 +359,11 @@ export default function OrdersTab({ orders, customers, brandId, onMsg, onRefresh
)}
{/* Invoice download — always visible as icon button */}
<a
<a aria-label="Download Invoice"
href={`/api/wholesale/invoice/${o.id}/pdf`}
target="_blank"
rel="noopener noreferrer"
title="Download Invoice"
className="inline-flex items-center justify-center rounded-xl w-9 h-9 text-[var(--admin-text-muted)] hover:text-[var(--admin-accent)] hover:bg-[var(--admin-bg-subtle)] transition-colors"
>
rel="noopener noreferrer" title="Download Invoice"
className="inline-flex items-center justify-center rounded-xl w-9 h-9 text-[var(--admin-text-muted)] hover:text-[var(--admin-accent)] hover:bg-[var(--admin-bg-subtle)] transition-colors">
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75}><path strokeLinecap="round" strokeLinejoin="round" d="M9 12h9m-9-6h6m-3 12a9 9 0 110 12H15M6 2h9l4 4v14a2 2 0 01-2 2H6a2 2 0 01-2-2V4a2 2 0 012-2z"/></svg>
</a>
@@ -463,13 +461,13 @@ export default function OrdersTab({ orders, customers, brandId, onMsg, onRefresh
<h3 className="font-semibold text-[var(--admin-text-primary)] mb-4">Record Deposit</h3>
<div className="space-y-3">
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Amount ($)</label>
<input type="number" value={depAmount} onChange={e => setDepAmount(e.target.value)}
<label htmlFor="fld-4-amount" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Amount ($)</label>
<input id="fld-4-amount" type="number" value={depAmount} onChange={e => setDepAmount(e.target.value)}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" step="0.01" />
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Method</label>
<select value={depMethod} onChange={e => setDepMethod(e.target.value)}
<label htmlFor="fld-5-method" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Method</label>
<select id="fld-5-method" value={depMethod} onChange={e => setDepMethod(e.target.value)}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]">
<option value="cash">Cash</option>
<option value="check">Check</option>
@@ -497,13 +495,13 @@ export default function OrdersTab({ orders, customers, brandId, onMsg, onRefresh
<h3 className="font-semibold text-[var(--admin-text-primary)] mb-4">Bulk Record Deposit ({selected.size} orders)</h3>
<div className="space-y-3">
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Amount per order ($)</label>
<input type="number" value={bulkDepAmount} onChange={e => setBulkDepAmount(e.target.value)}
<label htmlFor="fld-6-amount-per-order" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Amount per order ($)</label>
<input id="fld-6-amount-per-order" type="number" value={bulkDepAmount} onChange={e => setBulkDepAmount(e.target.value)}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" step="0.01" placeholder="0.00" />
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Method</label>
<select value={bulkDepMethod} onChange={e => setBulkDepMethod(e.target.value)}
<label htmlFor="fld-7-method-2" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Method</label>
<select id="fld-7-method-2" value={bulkDepMethod} onChange={e => setBulkDepMethod(e.target.value)}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]">
<option value="cash">Cash</option>
<option value="check">Check</option>
+8 -10
View File
@@ -142,8 +142,8 @@ export default function ProductsTab({ products, brandId, onMsg, onRefresh }: Pro
</select>
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Availability</label>
<select value={form.availability} onChange={e => setForm(f => ({ ...f, availability: e.target.value }))}
<label htmlFor="fld-1-availability" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Availability</label>
<select id="fld-1-availability" value={form.availability} onChange={e => setForm(f => ({ ...f, availability: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]">
<option value="available">Available</option>
<option value="unavailable">Unavailable</option>
@@ -151,20 +151,18 @@ export default function ProductsTab({ products, brandId, onMsg, onRefresh }: Pro
</select>
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Qty Available</label>
<input type="number" value={form.qtyAvailable} onChange={e => setForm(f => ({ ...f, qtyAvailable: Number(e.target.value) }))}
<label htmlFor="fld-2-qty-available" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Qty Available</label>
<input id="fld-2-qty-available" type="number" value={form.qtyAvailable} onChange={e => setForm(f => ({ ...f, qtyAvailable: Number(e.target.value) }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" />
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">HP SKU</label>
<input value={form.hpSku} onChange={e => setForm(f => ({ ...f, hpSku: e.target.value }))}
<label htmlFor="fld-3-hp-sku" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">HP SKU</label>
<input id="fld-3-hp-sku" value={form.hpSku} onChange={e => setForm(f => ({ ...f, hpSku: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" />
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">
Price Tiers (JSON array of {"{\"min_qty\":1,\"max_qty\":24,\"price\":20}"})
</label>
<input value={form.priceTiers} onChange={e => setForm(f => ({ ...f, priceTiers: e.target.value }))}
<label htmlFor="fld-4-price-tiers-json-array-of-min-qty1max-qty24price20" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Price Tiers (JSON array of {"{\"min_qty\":1,\"max_qty\":24,\"price\":20}"})</label>
<input id="fld-4-price-tiers-json-array-of-min-qty1max-qty24price20" value={form.priceTiers} onChange={e => setForm(f => ({ ...f, priceTiers: e.target.value }))}
placeholder='[{"min_qty":1,"max_qty":24,"price":20},{"min_qty":25,"max_qty":0,"price":18}]'
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm font-mono outline-none focus:border-[var(--admin-accent)]" />
</div>
+18 -18
View File
@@ -170,48 +170,48 @@ export default function SettingsTab({ settings, brandId, onMsg, onRefresh, canMa
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Minimum Order Amount ($)</label>
<input type="number" value={form.minOrderAmount} onChange={e => setForm(f => ({ ...f, minOrderAmount: e.target.value }))}
<label htmlFor="fld-1-minimum-order-amount" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Minimum Order Amount ($)</label>
<input id="fld-1-minimum-order-amount" type="number" value={form.minOrderAmount} onChange={e => setForm(f => ({ ...f, minOrderAmount: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" step="0.01" placeholder="No minimum" />
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">From Email</label>
<input type="email" value={form.fromEmail} onChange={e => setForm(f => ({ ...f, fromEmail: e.target.value }))}
<label htmlFor="fld-2-from-email" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">From Email</label>
<input id="fld-2-from-email" type="email" value={form.fromEmail} onChange={e => setForm(f => ({ ...f, fromEmail: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" />
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Pickup Location</label>
<textarea value={form.pickupLocation} onChange={e => setForm(f => ({ ...f, pickupLocation: e.target.value }))}
<label htmlFor="fld-3-pickup-location" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Pickup Location</label>
<textarea id="fld-3-pickup-location" value={form.pickupLocation} onChange={e => setForm(f => ({ ...f, pickupLocation: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)] font-mono" rows={3} />
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">FOB Location</label>
<input value={form.fobLocation} onChange={e => setForm(f => ({ ...f, fobLocation: e.target.value }))}
<label htmlFor="fld-4-fob-location" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">FOB Location</label>
<input id="fld-4-fob-location" value={form.fobLocation} onChange={e => setForm(f => ({ ...f, fobLocation: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" />
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Name</label>
<input value={form.invoiceBusinessName} onChange={e => setForm(f => ({ ...f, invoiceBusinessName: e.target.value }))}
<label htmlFor="fld-5-invoice-business-name" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Name</label>
<input id="fld-5-invoice-business-name" value={form.invoiceBusinessName} onChange={e => setForm(f => ({ ...f, invoiceBusinessName: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" />
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Address</label>
<textarea value={form.invoiceBusinessAddress} onChange={e => setForm(f => ({ ...f, invoiceBusinessAddress: e.target.value }))}
<label htmlFor="fld-6-invoice-business-address" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Address</label>
<textarea id="fld-6-invoice-business-address" value={form.invoiceBusinessAddress} onChange={e => setForm(f => ({ ...f, invoiceBusinessAddress: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" rows={2} />
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Phone</label>
<input value={form.invoiceBusinessPhone} onChange={e => setForm(f => ({ ...f, invoiceBusinessPhone: e.target.value }))}
<label htmlFor="fld-7-invoice-business-phone" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Phone</label>
<input id="fld-7-invoice-business-phone" value={form.invoiceBusinessPhone} onChange={e => setForm(f => ({ ...f, invoiceBusinessPhone: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" />
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Email</label>
<input type="email" value={form.invoiceBusinessEmail} onChange={e => setForm(f => ({ ...f, invoiceBusinessEmail: e.target.value }))}
<label htmlFor="fld-8-invoice-business-email" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Email</label>
<input id="fld-8-invoice-business-email" type="email" value={form.invoiceBusinessEmail} onChange={e => setForm(f => ({ ...f, invoiceBusinessEmail: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" />
</div>
<div className="col-span-2">
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Website</label>
<input value={form.invoiceBusinessWebsite} onChange={e => setForm(f => ({ ...f, invoiceBusinessWebsite: e.target.value }))}
<label htmlFor="fld-9-invoice-business-website" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Invoice Business Website</label>
<input id="fld-9-invoice-business-website" value={form.invoiceBusinessWebsite} onChange={e => setForm(f => ({ ...f, invoiceBusinessWebsite: e.target.value }))}
className="w-full rounded-xl border border-[var(--admin-border)] px-3 py-2 text-sm outline-none focus:border-[var(--admin-accent)]" placeholder="https://" />
</div>
@@ -114,8 +114,8 @@ export default function WebhookSettingsSection({ brandId, onMsg }: WebhookSettin
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Webhook URL</label>
<input
<label htmlFor="fld-1-webhook-url" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Webhook URL</label>
<input id="fld-1-webhook-url"
type="url"
value={settings.url}
onChange={e => setSettings(s => ({ ...s, url: e.target.value }))}
@@ -125,10 +125,8 @@ export default function WebhookSettingsSection({ brandId, onMsg }: WebhookSettin
</div>
<div>
<label className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">
Signing Secret (HMAC-SHA256)
</label>
<input
<label htmlFor="fld-2-signing-secret-hmac-sha256" className="block text-sm font-medium text-[var(--admin-text-secondary)] mb-1">Signing Secret (HMAC-SHA256)</label>
<input id="fld-2-signing-secret-hmac-sha256"
type="password"
value={settings.secret}
onChange={e => setSettings(s => ({ ...s, secret: e.target.value }))}