Vendor-assign events and scope event catalog to vendor
Some checks failed
CI / Server — typecheck & build (push) Has been cancelled
CI / Client — typecheck & build (push) Has been cancelled
CI / Docker build (smoke test) (push) Has been cancelled

- Add vendorWhereClause() helper: admin + ?vendorId= filters to that
  vendor; admin with no filter sees all; other roles locked to own
- Fix events GET / to use vendorWhereClause so vendor filter works
- EventFormModal: admin sees a Vendor picker when creating a new event,
  pre-populated from the active VendorFilter; POST includes ?vendorId=
- EventConfigPanel: scope /taxes and /products fetches to event.vendorId
  so only the event's vendor's catalog items are selectable

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 17:48:44 -05:00
parent e1b1a82e07
commit 31e539102b
3 changed files with 58 additions and 8 deletions

View File

@@ -5,7 +5,7 @@ import { PageHeader } from "../components/PageHeader";
import { VendorFilter } from "../components/VendorFilter";
import { Table } from "../components/Table";
import { Modal } from "../components/Modal";
import { FormField, Btn } from "../components/FormField";
import { FormField, Btn, inputStyle } from "../components/FormField";
// ─── Types ──────────────────────────────────────────────────────────────────
@@ -139,6 +139,7 @@ export default function EventsPage() {
{showForm && (
<EventFormModal
event={editing}
defaultVendorId={vendorId}
onClose={() => setShowForm(false)}
onSaved={() => { setShowForm(false); load(); }}
/>
@@ -159,20 +160,39 @@ export default function EventsPage() {
// ─── Event Form Modal ────────────────────────────────────────────────────────
function EventFormModal({ event, onClose, onSaved }: {
function EventFormModal({ event, defaultVendorId, onClose, onSaved }: {
event: Event | null;
defaultVendorId?: string;
onClose: () => void;
onSaved: () => void;
}) {
const { user } = useAuth();
const isAdmin = user?.role === "admin";
const [name, setName] = useState(event?.name ?? "");
const [description, setDescription] = useState(event?.description ?? "");
const [startsAt, setStartsAt] = useState(event ? toDatetimeLocal(event.startsAt) : "");
const [endsAt, setEndsAt] = useState(event ? toDatetimeLocal(event.endsAt) : "");
const [isActive, setIsActive] = useState(event?.isActive ?? true);
const [selectedVendorId, setSelectedVendorId] = useState(
event?.vendorId ?? defaultVendorId ?? ""
);
const [vendors, setVendors] = useState<Vendor[]>([]);
const [error, setError] = useState("");
const [saving, setSaving] = useState(false);
useEffect(() => {
if (isAdmin && !event) {
api.get<{ data: Vendor[] }>("/vendors?limit=200")
.then((r) => setVendors(r.data))
.catch(console.error);
}
}, [isAdmin, event]);
const save = async () => {
if (isAdmin && !event && !selectedVendorId) {
setError("Please select a vendor for this event.");
return;
}
setSaving(true);
setError("");
try {
@@ -185,7 +205,8 @@ function EventFormModal({ event, onClose, onSaved }: {
if (event) {
await api.put(`/events/${event.id}`, body);
} else {
await api.post("/events", body);
const q = isAdmin && selectedVendorId ? `?vendorId=${encodeURIComponent(selectedVendorId)}` : "";
await api.post(`/events${q}`, body);
}
onSaved();
} catch (err) {
@@ -197,6 +218,15 @@ function EventFormModal({ event, onClose, onSaved }: {
return (
<Modal title={event ? "Edit Event" : "New Event"} onClose={onClose}>
{isAdmin && !event && (
<FormField label="Vendor" required>
<select style={inputStyle} value={selectedVendorId}
onChange={(e) => setSelectedVendorId(e.target.value)} required>
<option value="">Select vendor</option>
{vendors.map((v) => <option key={v.id} value={v.id}>{v.name}</option>)}
</select>
</FormField>
)}
<FormField label="Name">
<input style={input} value={name} onChange={(e) => setName(e.target.value)} />
</FormField>
@@ -276,11 +306,12 @@ function EventConfigPanel({ event, onRefresh }: { event: Event; onRefresh: () =>
const [err, setErr] = useState("");
useEffect(() => {
const q = `?vendorId=${encodeURIComponent(event.vendorId)}&limit=200`;
Promise.all([
api.get<ApiList<Tax>>("/taxes?limit=100").then((r) => setTaxes(r.data)),
api.get<ApiList<Product>>("/products?limit=200").then((r) => setProducts(r.data)),
api.get<ApiList<Tax>>(`/taxes${q}`).then((r) => setTaxes(r.data)),
api.get<ApiList<Product>>(`/products${q}`).then((r) => setProducts(r.data)),
]).catch(console.error);
}, []);
}, [event.vendorId]);
const addTax = async () => {
setErr("");