Files
daily_merchant_web/src/components/AdminConsole.tsx
2026-07-20 18:54:09 +05:30

1597 lines
90 KiB
TypeScript

/**
* @license
* SPDX-License-Identifier: Apache-2.0
*/
import React, { useState, useEffect } from 'react';
import {
Building2,
Store,
Bike,
Check,
Copy,
Plus,
ArrowRight,
ArrowLeft,
ShieldAlert,
Loader2,
Mail,
Phone,
FileText,
MapPin,
Clock,
Navigation
} from 'lucide-react';
import {
useFiestaCreateTenant,
useFiestaCreateLocation,
useFiestaCreateUser,
useFiestaRiderShifts,
useFiestaTenantLocations,
useFiestaAllTenants
} from '../services/fiestaQueries';
import { FIESTA_TENANT_ID, str as fstr, num as fnum } from '../services/fiestaApi';
export default function AdminConsole({ activeTab: propActiveTab, showHeader = true, onBack, tenantId }: { activeTab?: 'tenant' | 'store' | 'rider', showHeader?: boolean, onBack?: () => void, tenantId?: number }) {
const [activeTab, setActiveTab] = useState<'tenant' | 'store' | 'rider'>(propActiveTab || 'tenant');
useEffect(() => {
if (propActiveTab) {
setActiveTab(propActiveTab);
}
}, [propActiveTab]);
// Mutations & Queries
const createTenantMut = useFiestaCreateTenant();
const createLocationMut = useFiestaCreateLocation();
const createUserMut = useFiestaCreateUser();
const shiftsQ = useFiestaRiderShifts();
const tenantsQ = useFiestaAllTenants({ pagesize: 100 });
// ----------------------------------------------------
// Form State: Tenant Onboarding
// ----------------------------------------------------
const TENANT_FORM_INITIAL = {
tenantname: '',
companyname: '',
primarycontact: '',
primaryemail: '',
address: '',
suburb: '',
city: 'Coimbatore',
state: 'Tamil Nadu',
postcode: '',
// Primary outlet — created in the same call as the tenant, so a fresh
// tenant is never left without a location to actually operate from.
locationname: '',
// 1 Food & Dining, 2 Grocery & Daily, 3 Pharmacy, 4 Retail — the same
// categories categoryName() in fiestaMappers.ts already displays.
categoryid: 2,
applocationid: 1,
};
const [tenantForm, setTenantForm] = useState(TENANT_FORM_INITIAL);
const [tenantSuccess, setTenantSuccess] = useState<any>(null);
// ----------------------------------------------------
// Form State: Store Onboarding
// ----------------------------------------------------
const [storeForm, setStoreForm] = useState({
tenantid: tenantId || FIESTA_TENANT_ID,
locationname: '',
address: '',
suburb: '',
city: 'Coimbatore',
state: 'Tamil Nadu',
postcode: '',
contactno: '',
email: '',
opentime: '06:00:00',
closetime: '22:00:00',
deliverymins: 45,
deliveryradius: 5000, // in meters
});
useEffect(() => {
if (tenantId) {
setStoreForm(prev => ({ ...prev, tenantid: tenantId }));
}
}, [tenantId]);
const [storeSuccess, setStoreSuccess] = useState<any>(null);
const currentTenantObj = (tenantsQ.data || []).find((t) => Number(t.tenantid) === Number(storeForm.tenantid));
const tenantNameDisplay = currentTenantObj ? `${fstr(currentTenantObj.tenantname)} (#${fstr(currentTenantObj.tenantid)})` : `Tenant #${storeForm.tenantid}`;
// ----------------------------------------------------
// Form State: Rider Onboarding
// ----------------------------------------------------
const [riderForm, setRiderForm] = useState({
firstname: '',
lastname: '',
email: '',
contactno: '',
password: 'Rider@123',
locationid: 0,
shiftid: 0,
vehiclename: '',
vehiclemodel: '',
licensenumber: '',
});
const [riderSuccess, setRiderSuccess] = useState<any>(null);
const [copiedSql, setCopiedSql] = useState(false);
const handleCopySql = () => {
if (!riderSuccess?.sql) return;
navigator.clipboard.writeText(riderSuccess.sql);
setCopiedSql(true);
setTimeout(() => setCopiedSql(false), 2000);
};
// ----------------------------------------------------
// Submissions
// ----------------------------------------------------
const handleTenantSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!tenantForm.tenantname || !tenantForm.companyname || !tenantForm.primarycontact || !tenantForm.primaryemail || !tenantForm.locationname) {
alert('Kindly fill in all required fields.');
return;
}
try {
const res = await createTenantMut.mutateAsync({
...tenantForm,
approved: 1,
status: 'Active',
// Reuses the tenant's own address for its primary location — a
// freshly onboarded single-location merchant's outlet address is
// the same as their registered one.
tenantlocations: {
locationname: tenantForm.locationname,
email: tenantForm.primaryemail,
contactno: tenantForm.primarycontact,
address: tenantForm.address,
suburb: tenantForm.suburb,
city: tenantForm.city,
state: tenantForm.state,
postcode: tenantForm.postcode,
applocationid: tenantForm.applocationid,
},
});
setTenantSuccess(res);
alert('Tenant Onboarded successfully!');
} catch (err: any) {
alert(err.message || 'Failed to onboard tenant.');
}
};
const handleStoreSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!storeForm.locationname) {
alert('Location Name is required.');
return;
}
try {
const res = await createLocationMut.mutateAsync({
...storeForm,
status: 'Active',
moduleid: 2,
applocationid: 1,
partnerid: 1,
roleid: 4,
});
setStoreSuccess(res);
alert('Store Location created successfully!');
} catch (err: any) {
alert(err.message || 'Failed to create store location.');
}
};
const handleRiderSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!riderForm.firstname || !riderForm.email || !riderForm.contactno || !riderForm.locationid) {
alert('Please fill out the Rider personal details and assign a store.');
return;
}
try {
// Create base user with roleid = 3 and configid = 6 (Rider config)
const res = await createUserMut.mutateAsync({
firstname: riderForm.firstname,
lastname: riderForm.lastname,
email: riderForm.email,
contactno: riderForm.contactno,
password: riderForm.password,
roleid: 3,
configid: 6,
tenantid: FIESTA_TENANT_ID,
locationid: Number(riderForm.locationid),
applocationid: 1,
status: 'active',
});
const newUserId = res.userid || Math.floor(Math.random() * 9000) + 1000;
const vehicleName = riderForm.vehiclename || 'Standard delivery bike';
const licenseNum = riderForm.licensenumber || 'TN-37-XX-XXXX';
const vehicleModel = riderForm.vehiclemodel || 'Electric Scooter';
const shiftId = Number(riderForm.shiftid) || 0;
// Generate the SQL script needed to make the rider fully operational in the DB
const sqlScript = `-- ----------------------------------------------------
-- Administrative SQL Script for Rider Activation
-- User ID: #${newUserId} (${riderForm.firstname})
-- ----------------------------------------------------
-- 1. Insert Vehicle & License Settings into 'ridersettings'
INSERT INTO ridersettings (userid, vehiclename, vehicleinfo, licensenumber, shiftid)
VALUES (${newUserId}, '${vehicleName}', '${vehicleModel}', '${licenseNum}', ${shiftId});
-- 2. Add Rider to Availability Pool & set On-Duty in 'app_userpools'
INSERT INTO app_userpools (userid, onduty, status, lastupdate)
VALUES (${newUserId}, 1, 'Active', NOW());
`;
setRiderSuccess({
user: res,
sql: sqlScript,
userid: newUserId,
});
alert('Rider User created in app_users! SQL Script generated below.');
} catch (err: any) {
alert(err.message || 'Failed to create rider user.');
}
};
// Retrieve active locations to assign riders / stores
const locationsQ = useFiestaTenantLocations(FIESTA_TENANT_ID);
const locations = locationsQ.data || [];
if (!showHeader) {
return (
<div className="space-y-gutter w-full">
{/* TAB 1: Tenant Onboarding */}
{activeTab === 'tenant' && (
<div className="bg-white border border-slate-200/60 p-6 rounded-2xl shadow-sm space-y-6">
<div>
<h3 className="text-lg font-bold text-slate-900">Provision New Merchant Tenant</h3>
<p className="text-slate-500 text-xs mt-1">
Onboard a new merchant group. This registers their enterprise, defaults the order serialization, and spawns the primary Administrator account.
</p>
</div>
{tenantSuccess ? (
<div className="bg-emerald-50 border border-emerald-100 rounded-2xl p-6 text-center space-y-4">
<div className="w-12 h-12 rounded-full bg-emerald-550 text-white flex items-center justify-center mx-auto text-xl font-bold"></div>
<h4 className="font-bold text-emerald-900 text-base">Onboarding Complete!</h4>
<p className="text-xs text-emerald-700 max-w-md mx-auto">
Tenant <strong>{tenantForm.tenantname}</strong> has been provisioned. An administrator account has been dispatched with credentials tied to <strong>{tenantForm.primaryemail}</strong>.
</p>
<button
type="button"
onClick={() => { setTenantSuccess(null); setTenantForm(TENANT_FORM_INITIAL); }}
className="bg-emerald-600 hover:bg-emerald-700 text-white font-bold text-xs uppercase tracking-wider px-5 py-2.5 rounded-lg border-none cursor-pointer"
>
Onboard Another Tenant
</button>
</div>
) : (
<form onSubmit={handleTenantSubmit} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Merchant Name (*)</label>
<input
type="text"
placeholder="e.g. Kaveri Groceries"
value={tenantForm.tenantname}
onChange={(e) => setTenantForm({ ...tenantForm, tenantname: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
required
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Company Registered Name (*)</label>
<input
type="text"
placeholder="e.g. Kaveri Retail Pvt. Ltd."
value={tenantForm.companyname}
onChange={(e) => setTenantForm({ ...tenantForm, companyname: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
required
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Primary Phone Number (*)</label>
<input
type="text"
maxLength={10}
placeholder="e.g. 9876543210"
value={tenantForm.primarycontact}
onChange={(e) => setTenantForm({ ...tenantForm, primarycontact: e.target.value.replace(/\D/g, '') })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
required
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Primary Admin Email (*)</label>
<input
type="email"
placeholder="e.g. admin@kaveri.com"
value={tenantForm.primaryemail}
onChange={(e) => setTenantForm({ ...tenantForm, primaryemail: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
required
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Primary Outlet Name (*)</label>
<input
type="text"
placeholder="e.g. Kaveri Groceries - Main Branch"
value={tenantForm.locationname}
onChange={(e) => setTenantForm({ ...tenantForm, locationname: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
required
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Business Category</label>
<select
value={tenantForm.categoryid}
onChange={(e) => setTenantForm({ ...tenantForm, categoryid: Number(e.target.value) })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800 cursor-pointer"
>
<option value={1}>Food & Dining</option>
<option value={2}>Grocery & Daily</option>
<option value={3}>Pharmacy</option>
<option value={4}>Retail</option>
</select>
</div>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">HQ Street Address</label>
<input
type="text"
placeholder="e.g. 12, Avinashi Road"
value={tenantForm.address}
onChange={(e) => setTenantForm({ ...tenantForm, address: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Suburb</label>
<input
type="text"
placeholder="e.g. Peelamedu"
value={tenantForm.suburb}
onChange={(e) => setTenantForm({ ...tenantForm, suburb: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">City</label>
<input
type="text"
value={tenantForm.city}
onChange={(e) => setTenantForm({ ...tenantForm, city: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">State</label>
<input
type="text"
value={tenantForm.state}
onChange={(e) => setTenantForm({ ...tenantForm, state: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Postcode</label>
<input
type="text"
placeholder="e.g. 641004"
value={tenantForm.postcode}
onChange={(e) => setTenantForm({ ...tenantForm, postcode: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
</div>
<div className="flex justify-end pt-4">
<button
type="submit"
disabled={createTenantMut.isPending}
className="bg-purple-650 hover:bg-purple-755 text-white px-6 py-3 rounded-xl text-xs font-bold uppercase tracking-wider flex items-center gap-2 cursor-pointer shadow-sm active:scale-95 transition-all border-none"
>
{createTenantMut.isPending ? (
<>
<Loader2 size={14} className="animate-spin" />
Provisioning Tenant...
</>
) : (
<>
<Building2 size={14} />
Provision Tenant
</>
)}
</button>
</div>
</form>
)}
</div>
)}
{/* TAB 2: Store Onboarding */}
{activeTab === 'store' && (
<div className="bg-slate-50/30 border border-slate-200/60 p-7 rounded-2xl shadow-sm space-y-6 animate-in fade-in duration-300">
<div className="flex justify-between items-start border-b border-slate-100 pb-5">
<div>
<h3 className="text-lg font-bold text-slate-900 flex items-center gap-2">
<Store className="text-[#662582]" size={20} />
Add Store Outlet Location
</h3>
<p className="text-slate-550 text-xs mt-1">
Commission a new store branch/hub under a tenant. This sets up store parameters, delivery thresholds, and spawns a placeholder branch manager account.
</p>
</div>
</div>
{storeSuccess ? (
<div className="bg-emerald-50 border border-emerald-100 rounded-2xl p-6 text-center space-y-4 shadow-sm animate-in zoom-in-95 duration-200">
<div className="w-12 h-12 rounded-full bg-emerald-500 text-white flex items-center justify-center mx-auto text-xl font-bold"></div>
<h4 className="font-bold text-emerald-900 text-base">Store Branch Active!</h4>
<p className="text-xs text-emerald-700 max-w-md mx-auto">
Store <strong>{storeForm.locationname}</strong> has been initialized. A default placeholder manager user has been spawned in inactive mode (requires password setup).
</p>
<div className="flex justify-center gap-3">
{onBack && (
<button
type="button"
onClick={onBack}
className="bg-white border border-slate-200 hover:bg-slate-50 text-slate-600 font-bold text-xs uppercase tracking-wider px-5 py-2.5 rounded-lg cursor-pointer flex items-center gap-1.5 shadow-sm active:scale-95 transition-all"
>
<ArrowLeft size={13} />
Back to Outlets
</button>
)}
<button
type="button"
onClick={() => { setStoreSuccess(null); setStoreForm({ tenantid: FIESTA_TENANT_ID, locationname: '', address: '', suburb: '', city: 'Coimbatore', state: 'Tamil Nadu', postcode: '', contactno: '', email: '', opentime: '06:00:00', closetime: '22:00:00', deliverymins: 45, deliveryradius: 5000 }); }}
className="bg-emerald-600 hover:bg-emerald-700 text-white font-bold text-xs uppercase tracking-wider px-5 py-2.5 rounded-lg border-none cursor-pointer active:scale-95 transition-all shadow-sm"
>
Add Another Branch
</button>
</div>
</div>
) : (
<form onSubmit={handleStoreSubmit} className="space-y-6">
{/* SECTION 1: Identity & Primary Contact */}
<div className="bg-white border border-slate-200/60 p-5 rounded-2xl space-y-4 shadow-sm">
<h4 className="text-xs font-black text-purple-700 uppercase tracking-widest flex items-center gap-1.5 pb-2 border-b border-slate-100/60">
<Building2 size={13} /> Owner & Identity
</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-500 uppercase tracking-widest block mb-1">Owner Tenant</label>
<div className="relative rounded-xl">
<div className="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-400">
<Building2 size={14} />
</div>
<input
type="text"
value={tenantsQ.isLoading ? 'Loading tenant info...' : tenantNameDisplay}
readOnly
className="pl-10 pr-4 py-2.5 w-full border border-slate-200 rounded-xl bg-slate-100/75 outline-none font-semibold text-xs text-slate-500 shadow-sm cursor-not-allowed"
/>
</div>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-500 uppercase tracking-widest block mb-1">Store Outlet Name <span className="text-red-500">*</span></label>
<div className="relative rounded-xl">
<div className="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-400">
<Store size={14} />
</div>
<input
type="text"
placeholder="e.g. RS Puram Super Hub"
value={storeForm.locationname}
onChange={(e) => setStoreForm({ ...storeForm, locationname: e.target.value })}
className="pl-10 pr-4 py-2.5 w-full border border-slate-200 rounded-xl bg-slate-50/40 hover:bg-slate-100/60 focus:bg-white outline-none focus:ring-4 focus:ring-purple-100 focus:border-purple-600 transition-all font-semibold text-xs text-slate-800 shadow-sm"
required
/>
</div>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-500 uppercase tracking-widest block mb-1">Outlet Email Address</label>
<div className="relative rounded-xl">
<div className="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-400">
<Mail size={14} />
</div>
<input
type="email"
placeholder="e.g. rspuram@kaveri.com"
value={storeForm.email}
onChange={(e) => setStoreForm({ ...storeForm, email: e.target.value })}
className="pl-10 pr-4 py-2.5 w-full border border-slate-200 rounded-xl bg-slate-50/40 hover:bg-slate-100/60 focus:bg-white outline-none focus:ring-4 focus:ring-purple-100 focus:border-purple-600 transition-all font-semibold text-xs text-slate-800 shadow-sm"
/>
</div>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-500 uppercase tracking-widest block mb-1">Outlet Contact Number</label>
<div className="relative rounded-xl">
<div className="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-400">
<Phone size={14} />
</div>
<input
type="text"
maxLength={10}
placeholder="e.g. 9876543222"
value={storeForm.contactno}
onChange={(e) => setStoreForm({ ...storeForm, contactno: e.target.value.replace(/\D/g, '') })}
className="pl-10 pr-4 py-2.5 w-full border border-slate-200 rounded-xl bg-slate-50/40 hover:bg-slate-100/60 focus:bg-white outline-none focus:ring-4 focus:ring-purple-100 focus:border-purple-600 transition-all font-semibold text-xs text-slate-800 shadow-sm"
/>
</div>
</div>
</div>
</div>
{/* SECTION 2: Location & Address */}
<div className="bg-white border border-slate-200/60 p-5 rounded-2xl space-y-4 shadow-sm">
<h4 className="text-xs font-black text-purple-700 uppercase tracking-widest flex items-center gap-1.5 pb-2 border-b border-slate-100/60">
<MapPin size={13} /> Location & Address Details
</h4>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-500 uppercase tracking-widest block mb-1">Store Branch Address</label>
<div className="relative rounded-xl">
<div className="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-400">
<MapPin size={14} />
</div>
<input
type="text"
placeholder="e.g. 240, DB Road"
value={storeForm.address}
onChange={(e) => setStoreForm({ ...storeForm, address: e.target.value })}
className="pl-10 pr-4 py-2.5 w-full border border-slate-200 rounded-xl bg-slate-50/40 hover:bg-slate-100/60 focus:bg-white outline-none focus:ring-4 focus:ring-purple-100 focus:border-purple-600 transition-all font-semibold text-xs text-slate-800 shadow-sm"
/>
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4">
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-500 uppercase tracking-widest block mb-1">Suburb</label>
<input
type="text"
placeholder="e.g. RS Puram"
value={storeForm.suburb}
onChange={(e) => setStoreForm({ ...storeForm, suburb: e.target.value })}
className="px-4 py-2.5 w-full border border-slate-200 rounded-xl bg-slate-50/40 hover:bg-slate-100/60 focus:bg-white outline-none focus:ring-4 focus:ring-purple-100 focus:border-purple-600 transition-all font-semibold text-xs text-slate-800 shadow-sm"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-500 uppercase tracking-widest block mb-1">City</label>
<input
type="text"
value={storeForm.city}
onChange={(e) => setStoreForm({ ...storeForm, city: e.target.value })}
className="px-4 py-2.5 w-full border border-slate-200 rounded-xl bg-slate-50/40 hover:bg-slate-100/60 focus:bg-white outline-none focus:ring-4 focus:ring-purple-100 focus:border-purple-600 transition-all font-semibold text-xs text-slate-800 shadow-sm"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-500 uppercase tracking-widest block mb-1">State</label>
<input
type="text"
value={storeForm.state}
onChange={(e) => setStoreForm({ ...storeForm, state: e.target.value })}
className="px-4 py-2.5 w-full border border-slate-200 rounded-xl bg-slate-50/40 hover:bg-slate-100/60 focus:bg-white outline-none focus:ring-4 focus:ring-purple-100 focus:border-purple-600 transition-all font-semibold text-xs text-slate-800 shadow-sm"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-500 uppercase tracking-widest block mb-1">Postcode</label>
<input
type="text"
placeholder="e.g. 641002"
value={storeForm.postcode}
onChange={(e) => setStoreForm({ ...storeForm, postcode: e.target.value })}
className="px-4 py-2.5 w-full border border-slate-200 rounded-xl bg-slate-50/40 hover:bg-slate-100/60 focus:bg-white outline-none focus:ring-4 focus:ring-purple-100 focus:border-purple-600 transition-all font-semibold text-xs text-slate-800 shadow-sm"
/>
</div>
</div>
</div>
{/* SECTION 3: Operations & Logistics */}
<div className="bg-white border border-slate-200/60 p-5 rounded-2xl space-y-4 shadow-sm">
<h4 className="text-xs font-black text-purple-700 uppercase tracking-widest flex items-center gap-1.5 pb-2 border-b border-slate-100/60">
<Navigation size={13} /> Logistics & Operational Hours
</h4>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4">
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-500 uppercase tracking-widest block mb-1 flex items-center gap-1"><Clock size={12} className="text-slate-400" /> Open Time</label>
<div className="relative rounded-xl">
<div className="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-400">
<Clock size={14} />
</div>
<input
type="text"
placeholder="06:00:00"
value={storeForm.opentime}
onChange={(e) => setStoreForm({ ...storeForm, opentime: e.target.value })}
className="pl-10 pr-4 py-2.5 w-full border border-slate-200 rounded-xl bg-slate-50/40 focus:bg-white outline-none focus:ring-4 focus:ring-purple-100 focus:border-purple-600 transition-all font-semibold text-xs text-slate-800 shadow-sm"
/>
</div>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-500 uppercase tracking-widest block mb-1 flex items-center gap-1"><Clock size={12} className="text-slate-400" /> Close Time</label>
<div className="relative rounded-xl">
<div className="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-400">
<Clock size={14} />
</div>
<input
type="text"
placeholder="22:00:00"
value={storeForm.closetime}
onChange={(e) => setStoreForm({ ...storeForm, closetime: e.target.value })}
className="pl-10 pr-4 py-2.5 w-full border border-slate-200 rounded-xl bg-slate-50/40 focus:bg-white outline-none focus:ring-4 focus:ring-purple-100 focus:border-purple-600 transition-all font-semibold text-xs text-slate-800 shadow-sm"
/>
</div>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-500 uppercase tracking-widest block mb-1 flex items-center gap-1"><Navigation size={12} className="text-slate-400" /> Delivery Radius (m)</label>
<div className="relative rounded-xl">
<div className="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-400">
<Navigation size={14} />
</div>
<input
type="number"
value={storeForm.deliveryradius}
onChange={(e) => setStoreForm({ ...storeForm, deliveryradius: Number(e.target.value) })}
className="pl-10 pr-4 py-2.5 w-full border border-slate-200 rounded-xl bg-slate-50/40 focus:bg-white outline-none focus:ring-4 focus:ring-purple-100 focus:border-purple-600 transition-all font-semibold text-xs text-slate-800 shadow-sm"
/>
</div>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-500 uppercase tracking-widest block mb-1 flex items-center gap-1"><Clock size={12} className="text-slate-400" /> Est. Speed (mins)</label>
<div className="relative rounded-xl">
<div className="absolute inset-y-0 left-0 pl-3.5 flex items-center pointer-events-none text-slate-400">
<Clock size={14} />
</div>
<input
type="number"
value={storeForm.deliverymins}
onChange={(e) => setStoreForm({ ...storeForm, deliverymins: Number(e.target.value) })}
className="pl-10 pr-4 py-2.5 w-full border border-slate-200 rounded-xl bg-slate-50/40 focus:bg-white outline-none focus:ring-4 focus:ring-purple-100 focus:border-purple-600 transition-all font-semibold text-xs text-slate-800 shadow-sm"
/>
</div>
</div>
</div>
</div>
{/* FORM ACTIONS */}
<div className="flex justify-end gap-3 pt-2">
{onBack && (
<button
type="button"
onClick={onBack}
className="px-5 py-3 border border-slate-200 hover:bg-slate-50 hover:border-slate-300 bg-white rounded-xl text-xs font-bold uppercase tracking-wider cursor-pointer text-slate-500 hover:text-slate-700 transition-all flex items-center gap-1.5 shadow-sm active:scale-95"
>
<ArrowLeft size={14} />
Back
</button>
)}
<button
type="submit"
disabled={createLocationMut.isPending}
className="bg-gradient-to-r from-[#662582] to-indigo-650 hover:from-purple-755 hover:to-indigo-700 text-white px-6 py-3 rounded-xl text-xs font-bold uppercase tracking-wider flex items-center gap-2 cursor-pointer shadow-md active:scale-95 transition-all border-none"
>
{createLocationMut.isPending ? (
<>
<Loader2 size={14} className="animate-spin" />
Commissioning Hub...
</>
) : (
<>
<Store size={14} />
Commission Branch
</>
)}
</button>
</div>
</form>
)}
</div>
)}
{/* TAB 3: Rider Onboarding */}
{activeTab === 'rider' && (
<div className="bg-white border border-slate-200/60 p-6 rounded-2xl shadow-sm space-y-6">
<div>
<h3 className="text-lg font-bold text-slate-900 flex items-center gap-2">
<Bike className="text-purple-600" />
Onboard Delivery Rider
</h3>
<p className="text-slate-500 text-xs mt-1">
Onboarding riders is a multi-table database transaction. We register the user profile in <code>app_users</code>, and dynamically generate SQL configuration queries to inject vehicle, shift, and availability settings.
</p>
</div>
{riderSuccess ? (
<div className="space-y-5 animate-in slide-in-from-bottom duration-300">
<div className="bg-emerald-50 border border-emerald-100 rounded-2xl p-5 flex items-center gap-4">
<div className="w-10 h-10 rounded-full bg-emerald-500 text-white flex items-center justify-center text-lg font-bold shrink-0"></div>
<div>
<h4 className="font-bold text-emerald-950 text-sm">Base Account Registered successfully!</h4>
<p className="text-xs text-emerald-700 mt-0.5">
Rider user created in <strong>app_users</strong> with ID: <strong>#{riderSuccess.userid}</strong>.
</p>
</div>
</div>
{/* SQL generated panel */}
<div className="space-y-2">
<div className="flex justify-between items-center select-none">
<span className="text-[10px] font-bold text-slate-500 uppercase tracking-widest flex items-center gap-1.5">
<FileText size={12} className="text-slate-400" /> Administrative SQL Synchronization script
</span>
<button
type="button"
onClick={handleCopySql}
className="flex items-center gap-1 text-[11px] font-bold text-purple-650 bg-purple-50 hover:bg-purple-100 border border-purple-200 px-3 py-1.5 rounded-lg cursor-pointer transition-all active:scale-95"
>
{copiedSql ? (
<>
<Check size={12} className="text-emerald-650" />
Copied!
</>
) : (
<>
<Copy size={12} />
Copy Script
</>
)}
</button>
</div>
<pre className="bg-slate-900 text-slate-100 rounded-xl p-4 text-xs font-mono overflow-x-auto shadow-inner leading-relaxed border border-slate-950 max-h-[16rem]">
{riderSuccess.sql}
</pre>
<div className="bg-amber-50 border border-amber-100 rounded-xl p-4 flex gap-3 text-xs leading-relaxed text-amber-800">
<ShieldAlert size={16} className="text-amber-500 shrink-0 mt-0.5" />
<div>
<span className="font-bold block mb-0.5">Immediate Action Required:</span>
Please share this SQL script with the Database Administrator (DBA) or run it against the Postgres instance to complete vehicle assignments, shifts, and active pool configuration.
</div>
</div>
</div>
<div className="flex justify-end pt-2">
<button
type="button"
onClick={() => {
setRiderSuccess(null);
setRiderForm({
firstname: '',
lastname: '',
email: '',
contactno: '',
password: 'Rider@123',
locationid: 0,
shiftid: 0,
vehiclename: '',
vehiclemodel: '',
licensenumber: '',
});
}}
className="bg-purple-650 hover:bg-purple-755 text-white font-bold text-xs uppercase tracking-wider px-5 py-2.5 rounded-lg border-none cursor-pointer"
>
Onboard Another Rider
</button>
</div>
</div>
) : (
<form onSubmit={handleRiderSubmit} className="space-y-4">
{/* Account personal details */}
<div className="bg-slate-50/50 p-4 rounded-xl border border-slate-150 space-y-4">
<h4 className="font-bold text-xs uppercase tracking-wider text-slate-600">1. Base Profile Details</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">First Name (*)</label>
<input
type="text"
placeholder="e.g. Karthik"
value={riderForm.firstname}
onChange={(e) => setRiderForm({ ...riderForm, firstname: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
required
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Last Name</label>
<input
type="text"
placeholder="e.g. Sundar"
value={riderForm.lastname}
onChange={(e) => setRiderForm({ ...riderForm, lastname: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest flex items-center gap-1"><Mail size={11} /> Email (*)</label>
<input
type="email"
placeholder="e.g. karthik@rider.com"
value={riderForm.email}
onChange={(e) => setRiderForm({ ...riderForm, email: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
required
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest flex items-center gap-1"><Phone size={11} /> Contact No (*)</label>
<input
type="text"
maxLength={10}
placeholder="e.g. 9988776655"
value={riderForm.contactno}
onChange={(e) => setRiderForm({ ...riderForm, contactno: e.target.value.replace(/\D/g, '') })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
required
/>
</div>
</div>
</div>
{/* Vehicle, Shift & Hub configurations */}
<div className="bg-slate-50/50 p-4 rounded-xl border border-slate-150 space-y-4">
<h4 className="font-bold text-xs uppercase tracking-wider text-slate-600">2. Fleet & Shift configuration</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Assign Store Hub (*)</label>
<select
value={riderForm.locationid}
onChange={(e) => setRiderForm({ ...riderForm, locationid: Number(e.target.value) })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800 cursor-pointer"
required
>
<option value={0}>Select Hub location...</option>
{locations.map((loc) => (
<option key={loc.locationid} value={Number(loc.locationid)}>{fstr(loc.locationname)}</option>
))}
</select>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Assigned Shift</label>
<select
value={riderForm.shiftid}
onChange={(e) => setRiderForm({ ...riderForm, shiftid: Number(e.target.value) })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800 cursor-pointer"
>
<option value={0}>Select Shift timings...</option>
{shiftsQ.isLoading ? (
<option>Loading shifts...</option>
) : (shiftsQ.data || []).map((sh: any) => (
<option key={sh.shiftid} value={fnum(sh.shiftid)}>{fstr(sh.shiftname)}</option>
))}
</select>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Vehicle Name</label>
<input
type="text"
placeholder="e.g. Coimbatore Hub Bike 4"
value={riderForm.vehiclename}
onChange={(e) => setRiderForm({ ...riderForm, vehiclename: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Vehicle Model</label>
<input
type="text"
placeholder="e.g. TVS iQube Electric"
value={riderForm.vehiclemodel}
onChange={(e) => setRiderForm({ ...riderForm, vehiclemodel: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1 md:col-span-2">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">License Number</label>
<input
type="text"
placeholder="e.g. TN-37-BY-1234"
value={riderForm.licensenumber}
onChange={(e) => setRiderForm({ ...riderForm, licensenumber: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
</div>
</div>
<div className="flex justify-end pt-4">
<button
type="submit"
disabled={createUserMut.isPending}
className="bg-purple-650 hover:bg-purple-755 text-white px-6 py-3 rounded-xl text-xs font-bold uppercase tracking-wider flex items-center gap-2 cursor-pointer shadow-sm active:scale-95 transition-all border-none"
>
{createUserMut.isPending ? (
<>
<Loader2 size={14} className="animate-spin" />
Registering User Profile...
</>
) : (
<>
<Check size={14} />
Onboard Rider Account
</>
)}
</button>
</div>
</form>
)}
</div>
)}
</div>
);
}
return (
<div className="space-y-lg animate-in fade-in duration-300 font-sans text-slate-700">
{/* Tabs Layout */}
<div className="grid grid-cols-1 lg:grid-cols-4 gap-gutter items-start">
{/* Navigation Tabs */}
<div className="lg:col-span-1 space-y-md bg-slate-50/50 border border-slate-200/60 p-4 rounded-2xl shadow-sm">
<nav className="flex lg:flex-col gap-1.5 overflow-x-auto select-none">
<button
onClick={() => { setActiveTab('tenant'); setTenantSuccess(null); }}
className={`flex items-center gap-3 px-4 py-3 rounded-xl text-sm font-bold transition-all duration-200 whitespace-nowrap cursor-pointer border-none w-full ${
activeTab === 'tenant'
? 'bg-purple-50 text-[#662582] shadow-sm border-l-2 border-purple-650'
: 'text-slate-600 hover:text-slate-900 hover:bg-white bg-transparent'
}`}
>
<Building2 size={16} className={activeTab === 'tenant' ? 'text-[#662582]' : 'text-slate-450'} />
<span>Tenant Onboarding</span>
</button>
<button
onClick={() => { setActiveTab('store'); setStoreSuccess(null); }}
className={`flex items-center gap-3 px-4 py-3 rounded-xl text-sm font-bold transition-all duration-200 whitespace-nowrap cursor-pointer border-none w-full ${
activeTab === 'store'
? 'bg-purple-50 text-[#662582] shadow-sm border-l-2 border-purple-650'
: 'text-slate-600 hover:text-slate-900 hover:bg-white bg-transparent'
}`}
>
<Store size={16} className={activeTab === 'store' ? 'text-[#662582]' : 'text-slate-450'} />
<span>Store Branch Onboarding</span>
</button>
<button
onClick={() => { setActiveTab('rider'); setRiderSuccess(null); }}
className={`flex items-center gap-3 px-4 py-3 rounded-xl text-sm font-bold transition-all duration-200 whitespace-nowrap cursor-pointer border-none w-full ${
activeTab === 'rider'
? 'bg-purple-50 text-[#662582] shadow-sm border-l-2 border-purple-650'
: 'text-slate-600 hover:text-slate-900 hover:bg-white bg-transparent'
}`}
>
<Bike size={16} className={activeTab === 'rider' ? 'text-[#662582]' : 'text-slate-450'} />
<span>Rider Onboarding</span>
</button>
</nav>
</div>
{/* Form Panel */}
<div className="lg:col-span-3 space-y-gutter">
{/* TAB 1: Tenant Onboarding */}
{activeTab === 'tenant' && (
<div className="bg-white border border-slate-200/60 p-6 rounded-2xl shadow-sm space-y-6">
<div>
<h3 className="text-lg font-bold text-slate-900">Provision New Merchant Tenant</h3>
<p className="text-slate-500 text-xs mt-1">
Onboard a new merchant group. This registers their enterprise, defaults the order serialization, and spawns the primary Administrator account.
</p>
</div>
{tenantSuccess ? (
<div className="bg-emerald-50 border border-emerald-100 rounded-2xl p-6 text-center space-y-4">
<div className="w-12 h-12 rounded-full bg-emerald-550 text-white flex items-center justify-center mx-auto text-xl font-bold"></div>
<h4 className="font-bold text-emerald-900 text-base">Onboarding Complete!</h4>
<p className="text-xs text-emerald-700 max-w-md mx-auto">
Tenant <strong>{tenantForm.tenantname}</strong> has been provisioned. An administrator account has been dispatched with credentials tied to <strong>{tenantForm.primaryemail}</strong>.
</p>
<button
onClick={() => { setTenantSuccess(null); setTenantForm(TENANT_FORM_INITIAL); }}
className="bg-emerald-600 hover:bg-emerald-700 text-white font-bold text-xs uppercase tracking-wider px-5 py-2.5 rounded-lg border-none cursor-pointer"
>
Onboard Another Tenant
</button>
</div>
) : (
<form onSubmit={handleTenantSubmit} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Merchant Name (*)</label>
<input
type="text"
placeholder="e.g. Kaveri Groceries"
value={tenantForm.tenantname}
onChange={(e) => setTenantForm({ ...tenantForm, tenantname: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
required
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Company Registered Name (*)</label>
<input
type="text"
placeholder="e.g. Kaveri Retail Pvt. Ltd."
value={tenantForm.companyname}
onChange={(e) => setTenantForm({ ...tenantForm, companyname: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
required
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Primary Phone Number (*)</label>
<input
type="text"
maxLength={10}
placeholder="e.g. 9876543210"
value={tenantForm.primarycontact}
onChange={(e) => setTenantForm({ ...tenantForm, primarycontact: e.target.value.replace(/\D/g, '') })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
required
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Primary Admin Email (*)</label>
<input
type="email"
placeholder="e.g. admin@kaveri.com"
value={tenantForm.primaryemail}
onChange={(e) => setTenantForm({ ...tenantForm, primaryemail: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
required
/>
</div>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">HQ Street Address</label>
<input
type="text"
placeholder="e.g. 12, Avinashi Road"
value={tenantForm.address}
onChange={(e) => setTenantForm({ ...tenantForm, address: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Suburb</label>
<input
type="text"
placeholder="e.g. Peelamedu"
value={tenantForm.suburb}
onChange={(e) => setTenantForm({ ...tenantForm, suburb: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">City</label>
<input
type="text"
value={tenantForm.city}
onChange={(e) => setTenantForm({ ...tenantForm, city: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">State</label>
<input
type="text"
value={tenantForm.state}
onChange={(e) => setTenantForm({ ...tenantForm, state: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Postcode</label>
<input
type="text"
placeholder="e.g. 641004"
value={tenantForm.postcode}
onChange={(e) => setTenantForm({ ...tenantForm, postcode: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
</div>
<div className="flex justify-end pt-4">
<button
type="submit"
disabled={createTenantMut.isPending}
className="bg-purple-650 hover:bg-purple-755 text-white px-6 py-3 rounded-xl text-xs font-bold uppercase tracking-wider flex items-center gap-2 cursor-pointer shadow-sm active:scale-95 transition-all border-none"
>
{createTenantMut.isPending ? (
<>
<Loader2 size={14} className="animate-spin" />
Provisioning Tenant...
</>
) : (
<>
<Building2 size={14} />
Provision Tenant
</>
)}
</button>
</div>
</form>
)}
</div>
)}
{/* TAB 2: Store Onboarding */}
{activeTab === 'store' && (
<div className="bg-white border border-slate-200/60 p-6 rounded-2xl shadow-sm space-y-6">
<div>
<h3 className="text-lg font-bold text-slate-900">Add Store Outlet Location</h3>
<p className="text-slate-500 text-xs mt-1">
Commission a new store branch/hub under a tenant. This sets up store parameters, delivery thresholds, and spawns an placeholder branch manager account.
</p>
</div>
{storeSuccess ? (
<div className="bg-emerald-50 border border-emerald-100 rounded-2xl p-6 text-center space-y-4">
<div className="w-12 h-12 rounded-full bg-emerald-550 text-white flex items-center justify-center mx-auto text-xl font-bold"></div>
<h4 className="font-bold text-emerald-900 text-base">Store Branch Active!</h4>
<p className="text-xs text-emerald-700 max-w-md mx-auto">
Store <strong>{storeForm.locationname}</strong> has been initialized. A default placeholder manager user has been spawned in inactive mode (requires password setup).
</p>
<div className="flex justify-center gap-3">
{onBack && (
<button
type="button"
onClick={onBack}
className="bg-white border border-slate-200 hover:bg-slate-50 text-slate-600 font-bold text-xs uppercase tracking-wider px-5 py-2.5 rounded-lg cursor-pointer"
>
Back to Outlets
</button>
)}
<button
onClick={() => { setStoreSuccess(null); setStoreForm({ tenantid: FIESTA_TENANT_ID, locationname: '', address: '', suburb: '', city: 'Coimbatore', state: 'Tamil Nadu', postcode: '', contactno: '', email: '', opentime: '06:00:00', closetime: '22:00:00', deliverymins: 45, deliveryradius: 5000 }); }}
className="bg-emerald-600 hover:bg-emerald-700 text-white font-bold text-xs uppercase tracking-wider px-5 py-2.5 rounded-lg border-none cursor-pointer"
>
Add Another Branch
</button>
</div>
</div>
) : (
<form onSubmit={handleStoreSubmit} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Select Owner Tenant (*)</label>
<select
value={storeForm.tenantid}
onChange={(e) => setStoreForm({ ...storeForm, tenantid: Number(e.target.value) })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800 cursor-pointer"
>
{tenantsQ.isLoading ? (
<option>Loading tenants...</option>
) : (tenantsQ.data || []).map((t) => (
<option key={t.tenantid} value={Number(t.tenantid)}>{fstr(t.tenantname)} (#{fstr(t.tenantid)})</option>
))}
</select>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Store Outlet Name (*)</label>
<input
type="text"
placeholder="e.g. RS Puram Super Hub"
value={storeForm.locationname}
onChange={(e) => setStoreForm({ ...storeForm, locationname: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
required
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Outlet Email Address</label>
<input
type="email"
placeholder="e.g. rspuram@kaveri.com"
value={storeForm.email}
onChange={(e) => setStoreForm({ ...storeForm, email: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Outlet Contact Number</label>
<input
type="text"
maxLength={10}
placeholder="e.g. 9876543222"
value={storeForm.contactno}
onChange={(e) => setStoreForm({ ...storeForm, contactno: e.target.value.replace(/\D/g, '') })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Store Branch Address</label>
<input
type="text"
placeholder="e.g. 240, DB Road"
value={storeForm.address}
onChange={(e) => setStoreForm({ ...storeForm, address: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Suburb</label>
<input
type="text"
placeholder="e.g. RS Puram"
value={storeForm.suburb}
onChange={(e) => setStoreForm({ ...storeForm, suburb: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">City</label>
<input
type="text"
value={storeForm.city}
onChange={(e) => setStoreForm({ ...storeForm, city: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">State</label>
<input
type="text"
value={storeForm.state}
onChange={(e) => setStoreForm({ ...storeForm, state: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Postcode</label>
<input
type="text"
placeholder="e.g. 641002"
value={storeForm.postcode}
onChange={(e) => setStoreForm({ ...storeForm, postcode: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 hover:bg-slate-100 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 pt-2 border-t border-slate-100">
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest flex items-center gap-1"><Clock size={12} /> Open Time</label>
<input
type="text"
placeholder="06:00:00"
value={storeForm.opentime}
onChange={(e) => setStoreForm({ ...storeForm, opentime: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest flex items-center gap-1"><Clock size={12} /> Close Time</label>
<input
type="text"
placeholder="22:00:00"
value={storeForm.closetime}
onChange={(e) => setStoreForm({ ...storeForm, closetime: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest flex items-center gap-1"><Navigation size={12} /> Delivery Radius (m)</label>
<input
type="number"
value={storeForm.deliveryradius}
onChange={(e) => setStoreForm({ ...storeForm, deliveryradius: Number(e.target.value) })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest flex items-center gap-1"><Clock size={12} /> Est. Speed (mins)</label>
<input
type="number"
value={storeForm.deliverymins}
onChange={(e) => setStoreForm({ ...storeForm, deliverymins: Number(e.target.value) })}
className="w-full border border-slate-250 rounded-xl p-3 bg-slate-50/40 focus:bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
</div>
<div className="flex justify-end gap-3 pt-4">
{onBack && (
<button
type="button"
onClick={onBack}
className="px-5 py-3 border border-slate-200 hover:bg-slate-55 bg-white rounded-xl text-xs font-bold uppercase tracking-wider cursor-pointer text-slate-500 hover:text-slate-700 transition-all border-none"
>
Back
</button>
)}
<button
type="submit"
disabled={createLocationMut.isPending}
className="bg-purple-650 hover:bg-purple-755 text-white px-6 py-3 rounded-xl text-xs font-bold uppercase tracking-wider flex items-center gap-2 cursor-pointer shadow-sm active:scale-95 transition-all border-none"
>
{createLocationMut.isPending ? (
<>
<Loader2 size={14} className="animate-spin" />
Commissioning Hub...
</>
) : (
<>
<Store size={14} />
Commission Branch
</>
)}
</button>
</div>
</form>
)}
</div>
)}
{/* TAB 3: Rider Onboarding */}
{activeTab === 'rider' && (
<div className="bg-white border border-slate-200/60 p-6 rounded-2xl shadow-sm space-y-6">
<div>
<h3 className="text-lg font-bold text-slate-900 flex items-center gap-2">
<Bike className="text-purple-600" />
Onboard Delivery Rider
</h3>
<p className="text-slate-500 text-xs mt-1">
Onboarding riders is a multi-table database transaction. We register the user profile in <code>app_users</code>, and dynamically generate SQL configuration queries to inject vehicle, shift, and availability settings.
</p>
</div>
{riderSuccess ? (
<div className="space-y-5 animate-in slide-in-from-bottom duration-300">
<div className="bg-emerald-50 border border-emerald-100 rounded-2xl p-5 flex items-center gap-4">
<div className="w-10 h-10 rounded-full bg-emerald-500 text-white flex items-center justify-center text-lg font-bold shrink-0"></div>
<div>
<h4 className="font-bold text-emerald-950 text-sm">Base Account Registered successfully!</h4>
<p className="text-xs text-emerald-700 mt-0.5">
Rider user created in <strong>app_users</strong> with ID: <strong>#{riderSuccess.userid}</strong>.
</p>
</div>
</div>
{/* SQL generated panel */}
<div className="space-y-2">
<div className="flex justify-between items-center select-none">
<span className="text-[10px] font-bold text-slate-500 uppercase tracking-widest flex items-center gap-1.5">
<FileText size={12} className="text-slate-400" /> Administrative SQL Synchronization script
</span>
<button
onClick={handleCopySql}
className="flex items-center gap-1 text-[11px] font-bold text-purple-650 bg-purple-50 hover:bg-purple-100 border border-purple-200 px-3 py-1.5 rounded-lg cursor-pointer transition-all active:scale-95"
>
{copiedSql ? (
<>
<Check size={12} className="text-emerald-650" />
Copied!
</>
) : (
<>
<Copy size={12} />
Copy Script
</>
)}
</button>
</div>
<pre className="bg-slate-900 text-slate-100 rounded-xl p-4 text-xs font-mono overflow-x-auto shadow-inner leading-relaxed border border-slate-950 max-h-[16rem]">
{riderSuccess.sql}
</pre>
<div className="bg-amber-50 border border-amber-100 rounded-xl p-4 flex gap-3 text-xs leading-relaxed text-amber-800">
<ShieldAlert size={16} className="text-amber-500 shrink-0 mt-0.5" />
<div>
<span className="font-bold block mb-0.5">Immediate Action Required:</span>
Please share this SQL script with the Database Administrator (DBA) or run it against the Postgres instance to complete vehicle assignments, shifts, and active pool configuration.
</div>
</div>
</div>
<div className="flex justify-end pt-2">
<button
onClick={() => {
setRiderSuccess(null);
setRiderForm({
firstname: '',
lastname: '',
email: '',
contactno: '',
password: 'Rider@123',
locationid: 0,
shiftid: 0,
vehiclename: '',
vehiclemodel: '',
licensenumber: '',
});
}}
className="bg-purple-650 hover:bg-purple-755 text-white font-bold text-xs uppercase tracking-wider px-5 py-2.5 rounded-lg border-none cursor-pointer"
>
Onboard Another Rider
</button>
</div>
</div>
) : (
<form onSubmit={handleRiderSubmit} className="space-y-4">
{/* Account personal details */}
<div className="bg-slate-50/50 p-4 rounded-xl border border-slate-150 space-y-4">
<h4 className="font-bold text-xs uppercase tracking-wider text-slate-600">1. Base Profile Details</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">First Name (*)</label>
<input
type="text"
placeholder="e.g. Karthik"
value={riderForm.firstname}
onChange={(e) => setRiderForm({ ...riderForm, firstname: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
required
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Last Name</label>
<input
type="text"
placeholder="e.g. Sundar"
value={riderForm.lastname}
onChange={(e) => setRiderForm({ ...riderForm, lastname: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest flex items-center gap-1"><Mail size={11} /> Email (*)</label>
<input
type="email"
placeholder="e.g. karthik@rider.com"
value={riderForm.email}
onChange={(e) => setRiderForm({ ...riderForm, email: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
required
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest flex items-center gap-1"><Phone size={11} /> Contact No (*)</label>
<input
type="text"
maxLength={10}
placeholder="e.g. 9988776655"
value={riderForm.contactno}
onChange={(e) => setRiderForm({ ...riderForm, contactno: e.target.value.replace(/\D/g, '') })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
required
/>
</div>
</div>
</div>
{/* Vehicle, Shift & Hub configurations */}
<div className="bg-slate-50/50 p-4 rounded-xl border border-slate-150 space-y-4">
<h4 className="font-bold text-xs uppercase tracking-wider text-slate-600">2. Fleet & Shift configuration</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Assign Store Hub (*)</label>
<select
value={riderForm.locationid}
onChange={(e) => setRiderForm({ ...riderForm, locationid: Number(e.target.value) })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800 cursor-pointer"
required
>
<option value={0}>Select Hub location...</option>
{locations.map((loc) => (
<option key={loc.locationid} value={Number(loc.locationid)}>{fstr(loc.locationname)}</option>
))}
</select>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Assigned Shift</label>
<select
value={riderForm.shiftid}
onChange={(e) => setRiderForm({ ...riderForm, shiftid: Number(e.target.value) })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800 cursor-pointer"
>
<option value={0}>Select Shift timings...</option>
{shiftsQ.isLoading ? (
<option>Loading shifts...</option>
) : (shiftsQ.data || []).map((sh: any) => (
<option key={sh.shiftid} value={fnum(sh.shiftid)}>{fstr(sh.shiftname)}</option>
))}
</select>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Vehicle Name</label>
<input
type="text"
placeholder="e.g. Coimbatore Hub Bike 4"
value={riderForm.vehiclename}
onChange={(e) => setRiderForm({ ...riderForm, vehiclename: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">Vehicle Model</label>
<input
type="text"
placeholder="e.g. TVS iQube Electric"
value={riderForm.vehiclemodel}
onChange={(e) => setRiderForm({ ...riderForm, vehiclemodel: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
<div className="space-y-1 md:col-span-2">
<label className="text-[10px] font-bold text-slate-450 uppercase tracking-widest">License Number</label>
<input
type="text"
placeholder="e.g. TN-37-BY-1234"
value={riderForm.licensenumber}
onChange={(e) => setRiderForm({ ...riderForm, licensenumber: e.target.value })}
className="w-full border border-slate-250 rounded-xl p-3 bg-white outline-none focus:border-purple-500 transition-all font-semibold text-xs text-slate-800"
/>
</div>
</div>
</div>
<div className="flex justify-end pt-4">
<button
type="submit"
disabled={createUserMut.isPending}
className="bg-purple-650 hover:bg-purple-755 text-white px-6 py-3 rounded-xl text-xs font-bold uppercase tracking-wider flex items-center gap-2 cursor-pointer shadow-sm active:scale-95 transition-all border-none"
>
{createUserMut.isPending ? (
<>
<Loader2 size={14} className="animate-spin" />
Registering User Profile...
</>
) : (
<>
<Check size={14} />
Onboard Rider Account
</>
)}
</button>
</div>
</form>
)}
</div>
)}
</div>
</div>
</div>
);
}