-
Seasonal Factors
- {result.seasonalFactors.map((f, i) =>
☀ {f}
)}
+
+
Seasonal Factors
+ {result.seasonalFactors.map((f, i) =>
☀ {f}
)}
)}
{result.riskFlags.length > 0 && (
-
-
Risk Flags
- {result.riskFlags.map((r, i) =>
⚠ {r}
)}
+
+
Risk Flags
+ {result.riskFlags.map((r, i) =>
⚠ {r}
)}
)}
-
@@ -1273,13 +1273,13 @@ type ModalProps = {
function ToolModal({ tool, brandId, brandName, onClose }: ModalProps) {
return (
-
-
+
+
{tool.icon}
-
{tool.title}
+ {tool.title}
-
+
@@ -1312,45 +1312,45 @@ export default function AIsettingsClient({ isConnected, brandId, brandName }: Pr
const availableCount = AI_TOOLS.filter((t) => t.status === "available").length;
return (
-
+
{/* Header */}
-
-
+
AI-assisted features across Harvest Reach, Products, Reports, and more.
{/* Connection Status Banner */}
{isConnected ? (
-
-
-
+
+
-
AI Connected
+
AI Connected
{availableCount} tool{availableCount !== 1 ? "s" : ""} ready to use
-
Active
+
Active
) : (
-
-
-
+
+
-
AI Not Configured
+
AI Not Configured
Add your API key to enable AI tools.
setActiveModule(null)}
className={`text-xs font-medium rounded-full px-3 py-1.5 transition-colors ${
- activeModule === null ? "bg-violet-600 text-white" : "bg-zinc-900 text-zinc-400 border border-zinc-800 hover:border-violet-300"
+ activeModule === null ? "bg-emerald-600 text-white" : "bg-white text-stone-500 border border-stone-300 hover:border-emerald-300"
}`}
>
All ({AI_TOOLS.length})
@@ -1379,7 +1379,7 @@ export default function AIsettingsClient({ isConnected, brandId, brandName }: Pr
key={m}
onClick={() => setActiveModule(activeModule === m ? null : m)}
className={`text-xs font-medium rounded-full px-3 py-1.5 transition-colors ${
- activeModule === m ? "bg-violet-600 text-white" : "bg-zinc-900 text-zinc-400 border border-zinc-800 hover:border-violet-300"
+ activeModule === m ? "bg-emerald-600 text-white" : "bg-white text-stone-500 border border-stone-300 hover:border-emerald-300"
}`}
>
{m} ({count})
@@ -1395,47 +1395,47 @@ export default function AIsettingsClient({ isConnected, brandId, brandName }: Pr
return (
{tool.icon}
-
{tool.title}
+ {tool.title}
{tool.badge && (
-
+
{tool.badge}
)}
{tool.status === "available" && (
- Ready
+ Ready
)}
{tool.status === "experimental" && (
- Experimental
+ Experimental
)}
{tool.status === "coming_soon" && (
- Coming Soon
+ Coming Soon
)}
-
{tool.module}
-
{tool.description}
+
{tool.module}
+
{tool.description}
-
+
{tool.status === "available" && (
isConnected && setActiveTool(tool)}
disabled={!isConnected}
className={`w-full rounded-xl px-4 py-2.5 text-sm font-semibold transition-colors ${
isConnected
- ? "bg-violet-600 text-white hover:bg-violet-700 cursor-pointer"
- : "bg-zinc-950 text-zinc-400 cursor-not-allowed"
+ ? "bg-emerald-600 text-white hover:bg-emerald-700 cursor-pointer"
+ : "bg-stone-50 text-stone-500 cursor-not-allowed"
}`}
title={!isConnected ? "Add your OpenAI API key in Settings to enable" : "Open tool"}
>
@@ -1443,12 +1443,12 @@ export default function AIsettingsClient({ isConnected, brandId, brandName }: Pr
)}
{tool.status === "coming_soon" && (
-
+
Coming Soon
)}
{tool.status === "experimental" && (
-
+
Request Access
)}
@@ -1459,9 +1459,9 @@ export default function AIsettingsClient({ isConnected, brandId, brandName }: Pr
{/* Info box */}
-
-
About AI in Route Commerce
-
+
+
About AI in Route Commerce
+
AI tools process your data locally — orders, products, stops, and contacts — and send requests to your configured AI provider.
No data is stored or shared beyond the generation request. Experimental features may require additional setup.
diff --git a/src/app/admin/settings/page.tsx b/src/app/admin/settings/page.tsx
index 5b542f7..eb13494 100644
--- a/src/app/admin/settings/page.tsx
+++ b/src/app/admin/settings/page.tsx
@@ -1,10 +1,7 @@
import { redirect } from "next/navigation";
import { getAdminUser } from "@/lib/admin-permissions";
import { getAdminUsers, getBrands } from "@/actions/admin/users";
-import TimeTrackingSettingsClient from "@/components/admin/TimeTrackingSettingsClient";
-import UsersPage from "@/components/admin/UsersPage";
-import SettingsSections from "@/components/admin/SettingsSections";
-import IntegrationsInner from "@/components/admin/IntegrationsInner";
+import SettingsClient from "@/components/admin/SettingsClient";
export default async function AdminSettingsPage() {
const adminUser = await getAdminUser();
@@ -17,75 +14,28 @@ export default async function AdminSettingsPage() {
getBrands(),
]);
+ // Breadcrumb nav (for page context)
+ const breadcrumb = (
+
+ );
+
return (
-
-
- {/* Header */}
-
-
-
Settings
-
Manage your brand, workers, tasks, users, and integrations.
-
-
- {/* Nav to anchor sections */}
-
-
- {/* Section 1: General Settings */}
-
-
- {/* Section 4: Users & Permissions */}
-
-
-
Users & Permissions
-
-
-
-
- {/* Section 5: Integrations */}
-
-
-
Integrations & Exports
-
-
- {/* Time Tracking Exports */}
-
-
Time Tracking Exports
-
-
-
- For more settings, see{" "}
- AI Tools
- {" "}and{" "}
- Shipping
- .
-
-
-
-
+
+ {breadcrumb}
+
);
}
\ No newline at end of file
diff --git a/src/components/admin/SettingsClient.tsx b/src/components/admin/SettingsClient.tsx
new file mode 100644
index 0000000..e8ce8d3
--- /dev/null
+++ b/src/components/admin/SettingsClient.tsx
@@ -0,0 +1,128 @@
+"use client";
+
+import { useState } from "react";
+import SettingsSections from "@/components/admin/SettingsSections";
+import UsersPage from "@/components/admin/UsersPage";
+import IntegrationsInner from "@/components/admin/IntegrationsInner";
+import TimeTrackingSettingsClient from "@/components/admin/TimeTrackingSettingsClient";
+
+type Tab = "general" | "users" | "integrations";
+
+const TABS: { id: Tab; label: string }[] = [
+ { id: "general", label: "General" },
+ { id: "users", label: "Users" },
+ { id: "integrations", label: "Integrations" },
+];
+
+type Brand = { id: string; name: string };
+
+type Props = {
+ brandId: string;
+ users: import("@/actions/admin/users").AdminUserRow[];
+ brands: Brand[];
+ currentUser: {
+ id: string;
+ role: string;
+ can_manage_users: boolean;
+ };
+};
+
+export default function SettingsClient({
+ brandId,
+ users,
+ brands,
+ currentUser,
+}: Props) {
+ const [activeTab, setActiveTab] = useState("general");
+
+ return (
+
+
+ {/* Header */}
+
+
+
+
+
Settings
+
Manage your brand, workers, tasks, users, and integrations.
+
+
+
+ {/* Tab nav */}
+
+
+
+
+
+ {/* Tab content */}
+ {activeTab === "general" && (
+
+
+
+ )}
+
+ {activeTab === "users" && (
+
+
+
Users & Permissions
+
+
+
+
+
+ )}
+
+ {activeTab === "integrations" && (
+
+
+
Integrations & Exports
+
+
+
+
+ {/* Time Tracking Exports */}
+
+
Time Tracking Exports
+
+
+
+
+ For more settings, see{" "}
+ AI Tools
+ {" "}and{" "}
+ Shipping
+ .
+
+
+
+ )}
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/route-trace/AdminLookupPage.tsx b/src/components/route-trace/AdminLookupPage.tsx
index 6924409..fb596bb 100644
--- a/src/components/route-trace/AdminLookupPage.tsx
+++ b/src/components/route-trace/AdminLookupPage.tsx
@@ -49,12 +49,12 @@ export default function AdminLookupPage({ brandId }: { brandId: string }) {
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="e.g. TC-20260519-001 or Sweet Corn"
- className="flex-1 rounded-xl border border-stone-200 bg-stone-50 px-4 py-3 text-base outline-none focus:border-stone-900 focus:bg-white transition-colors"
+ className="flex-1 rounded-xl border border-stone-200 bg-stone-50 px-4 py-3 text-base outline-none focus:border-emerald-600 focus:bg-white transition-colors"
/>
{loading ? "Searching..." : "🔍 Search"}
@@ -78,7 +78,7 @@ export default function AdminLookupPage({ brandId }: { brandId: string }) {
const input = prompt("Enter lot number from QR scan:");
if (input) handleScanResult(input.trim());
}}
- className="rounded-lg bg-stone-900 px-4 py-2 text-sm font-medium text-white hover:bg-stone-800"
+ className="rounded-lg bg-emerald-600 px-4 py-2 text-sm font-medium text-white hover:bg-emerald-700"
>
Enter Manually
diff --git a/src/components/route-trace/FsmaReportModal.tsx b/src/components/route-trace/FsmaReportModal.tsx
index 7de7238..27cabb8 100644
--- a/src/components/route-trace/FsmaReportModal.tsx
+++ b/src/components/route-trace/FsmaReportModal.tsx
@@ -51,7 +51,7 @@ export default function FsmaReportModal({ brandId }: Props) {
type="date"
value={startDate}
onChange={(e) => setStartDate(e.target.value)}
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-stone-900"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-emerald-600"
/>
@@ -60,7 +60,7 @@ export default function FsmaReportModal({ brandId }: Props) {
type="date"
value={endDate}
onChange={(e) => setEndDate(e.target.value)}
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-stone-900"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-emerald-600"
/>
@@ -78,7 +78,7 @@ export default function FsmaReportModal({ brandId }: Props) {
{loading ? "Preparing..." : "📥 Download CSV"}
diff --git a/src/components/route-trace/LotCreateForm.tsx b/src/components/route-trace/LotCreateForm.tsx
index b3238d8..d330d71 100644
--- a/src/components/route-trace/LotCreateForm.tsx
+++ b/src/components/route-trace/LotCreateForm.tsx
@@ -82,7 +82,7 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
onChange={(e) => setCropType(e.target.value)}
placeholder="e.g. Sweet Corn"
required
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-4 py-4 text-lg font-medium outline-none focus:border-stone-900 focus:bg-white transition-colors"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-4 py-4 text-lg font-medium outline-none focus:border-emerald-600 focus:bg-white transition-colors"
/>
@@ -92,7 +92,7 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
value={variety}
onChange={(e) => setVariety(e.target.value)}
placeholder="e.g. Golden Bantam"
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-4 py-4 text-lg font-medium outline-none focus:border-stone-900 focus:bg-white transition-colors"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-4 py-4 text-lg font-medium outline-none focus:border-emerald-600 focus:bg-white transition-colors"
/>
@@ -104,7 +104,7 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
value={harvest_date}
onChange={(e) => setHarvestDate(e.target.value)}
required
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-4 py-4 text-lg font-medium outline-none focus:border-stone-900 focus:bg-white transition-colors"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-4 py-4 text-lg font-medium outline-none focus:border-emerald-600 focus:bg-white transition-colors"
/>
@@ -120,7 +120,7 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
onChange={(e) => setFieldLocation(e.target.value)}
placeholder="e.g. North Field"
required
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-4 py-3.5 text-base outline-none focus:border-stone-900 focus:bg-white transition-colors"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-4 py-3.5 text-base outline-none focus:border-emerald-600 focus:bg-white transition-colors"
/>
@@ -131,7 +131,7 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
value={field_block}
onChange={(e) => setFieldBlock(e.target.value)}
placeholder="e.g. Block A"
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-stone-900 focus:bg-white transition-colors"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-emerald-600 focus:bg-white transition-colors"
/>
@@ -141,7 +141,7 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
value={worker_name}
onChange={(e) => setWorkerName(e.target.value)}
placeholder="e.g. Maria S."
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-stone-900 focus:bg-white transition-colors"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-emerald-600 focus:bg-white transition-colors"
/>
@@ -161,12 +161,12 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
placeholder="e.g. 4800"
min="0"
step="0.01"
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3.5 text-base outline-none focus:border-stone-900 focus:bg-white transition-colors"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3.5 text-base outline-none focus:border-emerald-600 focus:bg-white transition-colors"
/>
-
+
setCustomYieldUnit(e.target.value)}
- className="mt-1.5 w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-2 text-sm outline-none focus:border-stone-900 focus:bg-white transition-colors"
+ className="mt-1.5 w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-2 text-sm outline-none focus:border-emerald-600 focus:bg-white transition-colors"
/>
)}
@@ -215,7 +215,7 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
value={bin_id}
onChange={(e) => setBinId(e.target.value)}
placeholder="e.g. BIN-001"
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-stone-900 focus:bg-white transition-colors"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-emerald-600 focus:bg-white transition-colors"
/>
@@ -225,7 +225,7 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
value={container_id}
onChange={(e) => setContainerId(e.target.value)}
placeholder="e.g. CONT-A42"
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-stone-900 focus:bg-white transition-colors"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-emerald-600 focus:bg-white transition-colors"
/>
@@ -238,7 +238,7 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
onChange={(e) => setPallets(e.target.value)}
placeholder="e.g. 4"
min="0"
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-stone-900 focus:bg-white transition-colors"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-emerald-600 focus:bg-white transition-colors"
/>
@@ -248,7 +248,7 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
value={packer_name}
onChange={(e) => setPackerName(e.target.value)}
placeholder="e.g. Jose R."
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-stone-900 focus:bg-white transition-colors"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-emerald-600 focus:bg-white transition-colors"
/>
@@ -270,7 +270,7 @@ export default function LotCreateForm({ brandId }: { brandId: string }) {
onChange={(e) => setNotes(e.target.value)}
placeholder="Spray records, weather conditions, or other harvest details..."
rows={3}
- className="mt-3 w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-stone-900 focus:bg-white transition-colors resize-none"
+ className="mt-3 w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-emerald-600 focus:bg-white transition-colors resize-none"
/>
)}
diff --git a/src/components/route-trace/LotDetailPanel.tsx b/src/components/route-trace/LotDetailPanel.tsx
index 0296540..f49c752 100644
--- a/src/components/route-trace/LotDetailPanel.tsx
+++ b/src/components/route-trace/LotDetailPanel.tsx
@@ -145,7 +145,7 @@ export default function LotDetailPanel({
{nextStatuses.length > 0 && (
{ setNewStatus(nextStatuses[0]); setShowStatusModal(true); }}
- className="rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-blue-700 transition-colors"
+ className="rounded-xl bg-emerald-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-emerald-700 transition-colors"
>
Update Status
@@ -396,7 +396,7 @@ export default function LotDetailPanel({
value={newStatus}
onChange={(e) => setNewStatus(e.target.value)}
required
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-stone-900"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-emerald-600"
>
{STATUS_FLOW.filter((s) => s !== lot.status).map((s) => {
@@ -416,7 +416,7 @@ export default function LotDetailPanel({
value={location}
onChange={(e) => setLocation(e.target.value)}
placeholder="e.g. Warehouse A"
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-stone-900"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-emerald-600"
/>
@@ -426,7 +426,7 @@ export default function LotDetailPanel({
onChange={(e) => setNotes(e.target.value)}
placeholder="Any notes..."
rows={2}
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-stone-900 resize-none"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-emerald-600 resize-none"
/>
@@ -434,7 +434,7 @@ export default function LotDetailPanel({
setShowStatusModal(false)} className="rounded-xl border border-stone-200 px-4 py-2 text-sm font-semibold text-stone-600 hover:bg-stone-50">
Cancel
-
+
{isPending ? "Saving..." : "Save"}
@@ -461,7 +461,7 @@ export default function LotDetailPanel({
onChange={(e) => setBinId(e.target.value)}
placeholder="e.g. BIN-042"
required
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-stone-900"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-emerald-600"
/>
@@ -471,7 +471,7 @@ export default function LotDetailPanel({
onChange={(e) => setNotes(e.target.value)}
placeholder="Weight, count, or other details..."
rows={2}
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-stone-900 resize-none"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm outline-none focus:border-emerald-600 resize-none"
/>
@@ -508,7 +508,7 @@ export default function LotDetailPanel({
onChange={(e) => setUsedOrderId(e.target.value.toUpperCase())}
placeholder="e.g. 4f3e2a1b-..."
required
- className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm font-mono outline-none focus:border-stone-900"
+ className="w-full rounded-xl border border-stone-200 bg-stone-50 px-3 py-3 text-sm font-mono outline-none focus:border-emerald-600"
/>