/**
* @license
* SPDX-License-Identifier: Apache-2.0
*/
import React, { useEffect, useMemo, useRef, useState } from 'react';
import {
Building2,
Store,
Truck,
CreditCard,
SlidersHorizontal,
Users,
MapPin,
Phone,
Mail,
Check,
RotateCcw,
CheckCircle2,
} from 'lucide-react';
import { useFiestaAllTenants, useFiestaTenantLocations } from '../services/fiestaQueries';
import { FIESTA_TENANT_ID, str as fstr, num as fnum, roleName } from '../services/fiestaApi';
import UsersPanel from './UsersPanel';
interface SettingsViewProps {
tenantId?: number;
}
type TabKey = 'profile' | 'outlets' | 'users' | 'delivery' | 'payment' | 'preferences';
/** Locally-persisted merchant preferences (survive reload via localStorage). */
interface MerchantSettings {
// Business profile (seeded from live tenant data, then locally editable)
contactEmail: string;
contactPhone: string;
minOrderValue: number;
// Delivery
deliveryCharge: number;
prepMins: number;
deliveryWindowMins: number;
cancelWindowSecs: number;
autoAssignRider: boolean;
// Payment & tax
defaultTaxPercent: number;
codEnabled: boolean;
onlinePaymentEnabled: boolean;
// Preferences
defaultRegion: string;
defaultNewUserRole: number;
orderNotifications: boolean;
lowStockAlerts: boolean;
dailySummaryEmail: boolean;
syncInterval: number;
sandboxMode: boolean;
}
const STORAGE_KEY = 'merchant-settings-v1';
const DEFAULTS: MerchantSettings = {
contactEmail: '',
contactPhone: '',
minOrderValue: 0,
deliveryCharge: 30,
prepMins: 15,
deliveryWindowMins: 45,
cancelWindowSecs: 60,
autoAssignRider: true,
defaultTaxPercent: 5,
codEnabled: true,
onlinePaymentEnabled: true,
defaultRegion: 'Coimbatore',
defaultNewUserRole: 4,
orderNotifications: true,
lowStockAlerts: true,
dailySummaryEmail: false,
syncInterval: 5,
sandboxMode: false,
};
function loadSettings(): { settings: MerchantSettings; hadSaved: boolean } {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (raw) return { settings: { ...DEFAULTS, ...JSON.parse(raw) }, hadSaved: true };
} catch {
/* ignore corrupt storage */
}
return { settings: { ...DEFAULTS }, hadSaved: false };
}
// ── Small presentational helpers ────────────────────────────────────────────
function Toggle({ checked, onChange }: { checked: boolean; onChange: () => void }) {
return (
);
}
function Row({
title,
desc,
children,
}: {
title: string;
desc?: string;
children: React.ReactNode;
}) {
return (
);
}
const numberInputCls =
'w-24 border border-[#e2e8f0] rounded-lg p-1.5 text-right font-semibold text-zinc-700 bg-white outline-none focus:ring-1 focus:ring-[#581c87]';
const textInputCls =
'w-full border border-[#e2e8f0] rounded-lg p-sm bg-white outline-none focus:ring-1 focus:ring-[#581c87] text-zinc-700 font-medium';
const selectCls =
'border border-[#e2e8f0] bg-white rounded-lg p-1.5 font-semibold text-zinc-700 outline-none cursor-pointer';
export default function SettingsView({ tenantId = FIESTA_TENANT_ID }: SettingsViewProps) {
const [activeTab, setActiveTab] = useState('profile');
// Live tenant profile + outlets.
const tenantsQ = useFiestaAllTenants({ pagesize: 50 });
const tenant = (tenantsQ.data ?? []).find((t) => Number(t.tenantid) === tenantId) || null;
const locationsQ = useFiestaTenantLocations(tenantId);
const outlets = locationsQ.data ?? [];
// Persisted preferences.
const initial = useRef(loadSettings());
const [form, setForm] = useState(initial.current.settings);
const [saved, setSaved] = useState(initial.current.settings);
const [toast, setToast] = useState(null);
// First-run seeding: if nothing was saved yet, fill contact/min-order/region
// from the live tenant once it arrives.
const seededRef = useRef(initial.current.hadSaved);
useEffect(() => {
if (seededRef.current || !tenant) return;
seededRef.current = true;
const seed = (prev: MerchantSettings): MerchantSettings => ({
...prev,
contactEmail: prev.contactEmail || fstr(tenant.primaryemail),
contactPhone: prev.contactPhone || fstr(tenant.primarycontact),
minOrderValue: prev.minOrderValue || fnum(tenant.minorder),
defaultRegion: prev.defaultRegion || fstr(tenant.city) || 'Coimbatore',
});
setForm(seed);
setSaved(seed);
}, [tenant]);
const dirty = useMemo(() => JSON.stringify(form) !== JSON.stringify(saved), [form, saved]);
const set = (key: K, value: MerchantSettings[K]) =>
setForm((f) => ({ ...f, [key]: value }));
const handleSave = () => {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(form));
} catch {
/* ignore quota errors */
}
setSaved(form);
setToast('Settings saved');
window.setTimeout(() => setToast(null), 2200);
};
const handleReset = () => setForm(saved);
const tabs: Array<{ key: TabKey; label: string; icon: typeof Building2 }> = [
{ key: 'profile', label: 'Business Profile', icon: Building2 },
{ key: 'outlets', label: 'Outlets', icon: Store },
{ key: 'users', label: 'Users & Access', icon: Users },
{ key: 'delivery', label: 'Delivery', icon: Truck },
{ key: 'payment', label: 'Payment & Tax', icon: CreditCard },
{ key: 'preferences', label: 'Preferences', icon: SlidersHorizontal },
];
const roleOptions = [1, 2, 3, 4, 6];
return (
{/* Header */}
Settings
Manage your store profile, outlets, delivery, payments, and workspace preferences.
{tenantsQ.isLoading ? (
Loading live profile…
) : tenant ? (
Live · {fstr(tenant.tenantname)} · Tenant {tenantId}
) : (
Tenant profile unavailable
)}
{/* Tab rail */}
{/* Panel */}
{activeTab === 'profile' && (
Business Profile
{/* Live identity (read-only) */}
Store Name
{fstr(tenant?.tenantname) || '—'}
Legal / Company
{fstr(tenant?.companyname) || '—'}
Category
{fstr(tenant?.subcategoryname) || `Category ${fnum(tenant?.categoryid)}`}
Account Status
{fstr(tenant?.status) || '—'}
{fnum(tenant?.approved) === 1 ? 'Approved' : 'Pending'}
Registered Address
{fstr(tenant?.address) || '—'}
{tenant?.city ? ` · ${fstr(tenant.city)}, ${fstr(tenant.state)} ${fstr(tenant.postcode)}` : ''}
{/* Editable contact (persisted locally) */}
Identity fields above are read live from your tenant record. Contact details are saved to this workspace.
)}
{activeTab === 'outlets' && (
Outlet Locations
{locationsQ.isLoading ? 'Loading…' : `${outlets.length} outlet${outlets.length === 1 ? '' : 's'}`}
{locationsQ.isLoading ? (
Loading live outlets…
) : outlets.length === 0 ? (
No outlets found for this tenant.
) : (
{outlets.map((loc, i) => (
{fstr(loc.locationname)}
{fstr(loc.suburb)}, {fstr(loc.city)} {fstr(loc.postcode)}
{fstr(loc.status) || '—'}
Hours
{fstr(loc.opentime).slice(11, 16) || '—'}–{fstr(loc.closetime).slice(11, 16) || '—'}
Radius
{fnum(loc.deliveryradius)} m
ETA
{fnum(loc.deliverymins)} min
))}
)}
Outlets are read live from your tenant. Add or edit them in the Stores section.
)}
{activeTab === 'users' && (
)}
{activeTab === 'delivery' && (
)}
{activeTab === 'payment' && (
)}
{activeTab === 'preferences' && (
)}
{/* Save / Reset — lives with the settings card, not pinned to the screen.
Hidden on the Users tab, which manages accounts via the live API. */}
{dirty ? '● You have unsaved changes' : 'All changes saved'}
{/* Toast */}
{toast && (
{toast}
)}
);
}