Add multi-vendor capability with admin vendor management
- Add resolveVendorId() helper — admin can pass ?vendorId= to scope catalog operations to any vendor; other roles locked to JWT vendorId - Thread ?vendorId= through products, categories, taxes, events routes - Add DELETE /vendors/:id (admin only) with cascade-safe guard: blocks if vendor has users or transactions; otherwise cascade-deletes EventProduct → EventTax → Event → Product → Tax → Category → Vendor - Rewrite VendorPage: admin gets full CRUD list, vendor gets own settings - Add VendorFilter shared component (admin-only dropdown) - Integrate VendorFilter into Catalog, Users, and Events pages so admin can switch vendor context for all create/read operations Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import React, { useEffect, useState, useCallback } from "react";
|
||||
import { api } from "../api/client";
|
||||
import { useAuth } from "../context/AuthContext";
|
||||
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";
|
||||
@@ -50,6 +52,8 @@ interface EventSummary {
|
||||
// ─── Main Page ──────────────────────────────────────────────────────────────
|
||||
|
||||
export default function EventsPage() {
|
||||
const { user } = useAuth();
|
||||
const [vendorId, setVendorId] = useState(user?.vendorId ?? "");
|
||||
const [events, setEvents] = useState<Event[]>([]);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -61,7 +65,8 @@ export default function EventsPage() {
|
||||
const load = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await api.get<ApiList<Event>>("/events?limit=50");
|
||||
const q = vendorId && user?.role === "admin" ? `?vendorId=${encodeURIComponent(vendorId)}&limit=50` : "?limit=50";
|
||||
const res = await api.get<ApiList<Event>>(`/events${q}`);
|
||||
setEvents(res.data);
|
||||
setTotal(res.pagination.total);
|
||||
} catch (err) {
|
||||
@@ -69,7 +74,7 @@ export default function EventsPage() {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
}, [vendorId, user?.role]);
|
||||
|
||||
useEffect(() => { load(); }, [load]);
|
||||
|
||||
@@ -120,11 +125,14 @@ export default function EventsPage() {
|
||||
|
||||
return (
|
||||
<div style={{ padding: "32px 28px" }}>
|
||||
<PageHeader
|
||||
title="Events"
|
||||
subtitle={`${total} event${total !== 1 ? "s" : ""}`}
|
||||
action={<Btn onClick={() => { setEditing(null); setShowForm(true); }}>+ New Event</Btn>}
|
||||
/>
|
||||
<div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", flexWrap: "wrap", gap: 12, marginBottom: 4 }}>
|
||||
<PageHeader
|
||||
title="Events"
|
||||
subtitle={`${total} event${total !== 1 ? "s" : ""}`}
|
||||
action={<Btn onClick={() => { setEditing(null); setShowForm(true); }}>+ New Event</Btn>}
|
||||
/>
|
||||
<VendorFilter vendorId={vendorId} onChange={setVendorId} />
|
||||
</div>
|
||||
|
||||
<Table columns={columns} data={events} keyField="id" loading={loading} emptyText="No events yet." />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user