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