PageSourceSearch

https://epsmasdash-ms8uwkec.manus.space/assets/AgencyGroupManager-o_BX2-oj.js

js epsmasdash-ms8uwkec.manus.space collected 2026-10-03 05:02:19 UTC 19,248 bytes, 325 lines download raw bytes

1import { c as createLucideIcon, t as trpc, b as toast, r as reactExports, j as jsxRuntimeExports, n as Building2, B as Button, d as Badge, m as Users, a8 as UserPlus } from './index-CNT5CkK7.js';
2import { C as Card, b as CardHeader, c as CardTitle, d as CardDescription, a as CardContent } from './card-CV3CzesJ.js';
3import { I as Input } from './input-Dy4-W9m0.js';
4import { D as Dialog, b as DialogContent, c as DialogHeader, d as DialogTitle, e as DialogFooter } from './useComposition-DPCJbBnn.js';
5import { A as AlertDialog, a as AlertDialogContent, b as AlertDialogHeader, c as AlertDialogTitle, d as AlertDialogDescription, e as AlertDialogFooter, f as AlertDialogCancel, g as AlertDialogAction } from './alert-dialog-CZgE41L3.js';
6import { P as Plus } from './plus-BP2QxMPQ.js';
7import { P as Pen } from './pen-dQQbmgRv.js';
8import { T as Trash2 } from './trash-2-DkCMhEWr.js';
9
10/**
11 * @license lucide-react v0.453.0 - ISC
12 *
13 * This source code is licensed under the ISC license.
14 * See the LICENSE file in the root directory of this source tree.
15 */
16
17
18const UserMinus = createLucideIcon("UserMinus", [
19  ["path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2", key: "1yyitq" }],
20  ["circle", { cx: "9", cy: "7", r: "4", key: "nufk8" }],
21  ["line", { x1: "22", x2: "16", y1: "11", y2: "11", key: "1shjgl" }]
22]);
23
24const ALL_PRODUCERS = [
25  { key: "greg-fay", name: "Greg Fay" },
26  { key: "stacey-hinkle", name: "Stacey Hinkle" },
27  { key: "josh-hinkle", name: "Josh Hinkle" },
28  { key: "angie-hinkle", name: "Angie Hinkle" },
29  { key: "zach-hinkle", name: "Zach Hinkle" },
30  { key: "jon-bucher", name: "Jon Bucher" },
31  { key: "emily-bucher", name: "Emily Bucher" },
32  { key: "charlie-drain", name: "Charlie Drain" },
33  { key: "michael-jividen", name: "Michael Jividen" },
34  { key: "adam-kaufman", name: "Adam Kaufman" },
35  { key: "lesley-wiegand", name: "Lesley Wiegand" }
36];
37const COLOR_OPTIONS = [
38  "#1B3A6B",
39  "#0891b2",
40  "#16a34a",
41  "#dc2626",
42  "#9333ea",
43  "#ea580c",
44  "#0d9488",
45  "#d97706",
46  "#be185d",
47  "#475569"
48];
49function producerName(key) {
50  return ALL_PRODUCERS.find((p) => p.key === key)?.name ?? key;
51}
52function AgencyGroupManager() {
53  const utils = trpc.useUtils();
54  const { data: groups = [], isLoading } = trpc.agencyGroupManager.listGroups.useQuery();
55  const createGroup = trpc.agencyGroupManager.createGroup.useMutation({
56    onSuccess: () => {
57      utils.agencyGroupManager.listGroups.invalidate();
58      toast.success("Group created");
59      setShowCreate(false);
60      setNewGroupName("");
61      setNewGroupShort("");
62      setNewGroupColor("#1B3A6B");
63    },
64    onError: (e) => toast.error(e.message)
65  });
66  const updateGroup = trpc.agencyGroupManager.updateGroup.useMutation({
67    onSuccess: () => {
68      utils.agencyGroupManager.listGroups.invalidate();
69      toast.success("Group updated");
70      setEditingGroup(null);
71    },
72    onError: (e) => toast.error(e.message)
73  });
74  const deleteGroup = trpc.agencyGroupManager.deleteGroup.useMutation({
75    onSuccess: () => {
76      utils.agencyGroupManager.listGroups.invalidate();
77      toast.success("Group deleted");
78      setDeletingGroupId(null);
79    },
80    onError: (e) => toast.error(e.message)
81  });
82  const assignMember = trpc.agencyGroupManager.assignMember.useMutation({
83    onSuccess: () => {
84      utils.agencyGroupManager.listGroups.invalidate();
85    },
86    onError: (e) => toast.error(e.message)
87  });
88  const [showCreate, setShowCreate] = reactExports.useState(false);
89  const [newGroupName, setNewGroupName] = reactExports.useState("");
90  const [newGroupShort, setNewGroupShort] = reactExports.useState("");
91  const [newGroupColor, setNewGroupColor] = reactExports.useState("#1B3A6B");
92  const [editingGroup, setEditingGroup] = reactExports.useState(null);
93  const [deletingGroupId, setDeletingGroupId] = reactExports.useState(null);
94  const assignedKeys = new Set(groups.flatMap((g) => g.agentKeys));
95  const soloProducers = ALL_PRODUCERS.filter((p) => !assignedKeys.has(p.key));
96  function slugify(name) {
97    return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
98  }
99  function handleCreateGroup() {
100    if (!newGroupName.trim()) return;
101    createGroup.mutate({
102      groupId: slugify(newGroupName),
103      name: newGroupName.trim(),
104      shortName: newGroupShort.trim() || newGroupName.trim(),
105      color: newGroupColor
106    });
107  }
108  function handleMoveToGroup(agentKey, groupId) {
109    assignMember.mutate({ agentKey, groupId });
110  }
111  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6 p-6 max-w-4xl mx-auto", children: [
112    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
113      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
114        /* @__PURE__ */ jsxRuntimeExports.jsxs("h1", { className: "text-2xl font-bold text-foreground flex items-center gap-2", children: [
115          /* @__PURE__ */ jsxRuntimeExports.jsx(Building2, { className: "h-6 w-6 text-primary" }),
116          "Agency Group Manager"
117        ] }),
118        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Group producers so they can see each other's clients across all agents. Solo producers see only their own records." })
119      ] }),
120      /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { onClick: () => setShowCreate(true), className: "gap-2", children: [
121        /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "h-4 w-4" }),
122        "New Group"
123      ] })
124    ] }),
125    isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground text-sm", children: "Loading groups…" }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
126      groups.map((group) => /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, { className: "border-l-4", style: { borderLeftColor: group.color }, children: [
127        /* @__PURE__ */ jsxRuntimeExports.jsxs(CardHeader, { className: "pb-2", children: [
128          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
129            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
130              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-3 h-3 rounded-full", style: { backgroundColor: group.color } }),
131              /* @__PURE__ */ jsxRuntimeExports.jsx(CardTitle, { className: "text-base", children: group.name }),
132              /* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, { variant: "secondary", className: "text-xs", children: [
133                group.agentKeys.length,
134                " member",
135                group.agentKeys.length !== 1 ? "s" : ""
136              ] })
137            ] }),
138            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
139              /* @__PURE__ */ jsxRuntimeExports.jsx(
140                Button,
141                {
142                  size: "sm",
143                  variant: "ghost",
144                  className: "h-7 w-7 p-0",
145                  onClick: () => setEditingGroup({ groupId: group.groupId, name: group.name, shortName: group.shortName, color: group.color }),
146                  children: /* @__PURE__ */ jsxRuntimeExports.jsx(Pen, { className: "h-3.5 w-3.5" })
147                }
148              ),
149              /* @__PURE__ */ jsxRuntimeExports.jsx(
150                Button,
151                {
152                  size: "sm",
153                  variant: "ghost",
154                  className: "h-7 w-7 p-0 text-destructive hover:text-destructive",
155                  onClick: () => setDeletingGroupId(group.groupId),
156                  children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "h-3.5 w-3.5" })
157                }
158              )
159            ] })
160          ] }),
161          /* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, { className: "text-xs ml-6", children: "Members see each other's records across all 9 agents" })
162        ] }),
163        /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap gap-2", children: [
164          group.agentKeys.map((key) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 bg-muted rounded-full px-3 py-1 text-sm", children: [
165            /* @__PURE__ */ jsxRuntimeExports.jsx(Users, { className: "h-3.5 w-3.5 text-muted-foreground" }),
166            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: producerName(key) }),
167            /* @__PURE__ */ jsxRuntimeExports.jsx(
168              "button",
169              {
170                className: "ml-1 text-muted-foreground hover:text-destructive transition-colors",
171                title: "Make solo",
172                onClick: () => handleMoveToGroup(key, null),
173                children: /* @__PURE__ */ jsxRuntimeExports.jsx(UserMinus, { className: "h-3.5 w-3.5" })
174              }
175            )
176          ] }, key)),
177          soloProducers.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative group", children: [
178            /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { size: "sm", variant: "outline", className: "h-8 gap-1.5 text-xs rounded-full", children: [
179              /* @__PURE__ */ jsxRuntimeExports.jsx(UserPlus, { className: "h-3.5 w-3.5" }),
180              "Add Producer"
181            ] }),
182            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-full left-0 mt-1 z-50 hidden group-hover:block bg-popover border rounded-md shadow-lg min-w-[160px]", children: soloProducers.map((p) => /* @__PURE__ */ jsxRuntimeExports.jsx(
183              "button",
184              {
185                className: "w-full text-left px-3 py-2 text-sm hover:bg-accent transition-colors",
186                onClick: () => handleMoveToGroup(p.key, group.groupId),
187                children: p.name
188              },
189              p.key
190            )) })
191          ] })
192        ] }) })
193      ] }, group.groupId)),
194      /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, { className: "border-dashed", children: [
195        /* @__PURE__ */ jsxRuntimeExports.jsxs(CardHeader, { className: "pb-2", children: [
196          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
197            /* @__PURE__ */ jsxRuntimeExports.jsx(CardTitle, { className: "text-base text-muted-foreground", children: "Solo Producers" }),
198            /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "text-xs", children: soloProducers.length })
199          ] }),
200          /* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, { className: "text-xs", children: "These producers see only their own records across all agents" })
201        ] }),
202        /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { children: soloProducers.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground italic", children: "All producers are in a group." }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap gap-2", children: soloProducers.map((p) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 bg-muted/50 border border-dashed rounded-full px-3 py-1 text-sm text-muted-foreground", children: [
203          /* @__PURE__ */ jsxRuntimeExports.jsx(Users, { className: "h-3.5 w-3.5" }),
204          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: p.name }),
205          groups.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative group ml-1", children: [
206            /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "text-muted-foreground hover:text-primary transition-colors", title: "Add to group", children: /* @__PURE__ */ jsxRuntimeExports.jsx(UserPlus, { className: "h-3.5 w-3.5" }) }),
207            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-full left-0 mt-1 z-50 hidden group-hover:block bg-popover border rounded-md shadow-lg min-w-[160px]", children: groups.map((g) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
208              "button",
209              {
210                className: "w-full text-left px-3 py-2 text-sm hover:bg-accent transition-colors flex items-center gap-2",
211                onClick: () => handleMoveToGroup(p.key, g.groupId),
212                children: [
213                  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-2 h-2 rounded-full", style: { backgroundColor: g.color } }),
214                  g.name
215                ]
216              },
217              g.groupId
218            )) })
219          ] })
220        ] }, p.key)) }) })
221      ] })
222    ] }),
223    /* @__PURE__ */ jsxRuntimeExports.jsx(Dialog, { open: showCreate, onOpenChange: setShowCreate, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogContent, { children: [
224      /* @__PURE__ */ jsxRuntimeExports.jsx(DialogHeader, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(DialogTitle, { children: "Create New Agency Group" }) }),
225      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4 py-2", children: [
226        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
227          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-sm font-medium", children: "Agency Name" }),
228          /* @__PURE__ */ jsxRuntimeExports.jsx(
229            Input,
230            {
231              placeholder: "e.g. Hinkle Insurance",
232              value: newGroupName,
233              onChange: (e) => setNewGroupName(e.target.value)
234            }
235          )
236        ] }),
237        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
238          /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "text-sm font-medium", children: [
239            "Short Label ",
240            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground font-normal", children: "(shown in badges)" })
241          ] }),
242          /* @__PURE__ */ jsxRuntimeExports.jsx(
243            Input,
244            {
245              placeholder: "e.g. Josh / Angie",
246              value: newGroupShort,
247              onChange: (e) => setNewGroupShort(e.target.value)
248            }
249          )
250        ] }),
251        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
252          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-sm font-medium", children: "Color" }),
253          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex gap-2 flex-wrap", children: COLOR_OPTIONS.map((c) => /* @__PURE__ */ jsxRuntimeExports.jsx(
254            "button",
255            {
256              className: `w-7 h-7 rounded-full border-2 transition-all ${newGroupColor === c ? "border-foreground scale-110" : "border-transparent"}`,
257              style: { backgroundColor: c },
258              onClick: () => setNewGroupColor(c)
259            },
260            c
261          )) })
262        ] })
263      ] }),
264      /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogFooter, { children: [
265        /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "outline", onClick: () => setShowCreate(false), children: "Cancel" }),
266        /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { onClick: handleCreateGroup, disabled: !newGroupName.trim() || createGroup.isPending, children: createGroup.isPending ? "Creating…" : "Create Group" })
267      ] })
268    ] }) }),
269    /* @__PURE__ */ jsxRuntimeExports.jsx(Dialog, { open: !!editingGroup, onOpenChange: () => setEditingGroup(null), children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogContent, { children: [
270      /* @__PURE__ */ jsxRuntimeExports.jsx(DialogHeader, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(DialogTitle, { children: "Edit Group" }) }),
271      editingGroup && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4 py-2", children: [
272        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
273          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-sm font-medium", children: "Agency Name" }),
274          /* @__PURE__ */ jsxRuntimeExports.jsx(Input, { value: editingGroup.name, onChange: (e) => setEditingGroup((g) => g ? { ...g, name: e.target.value } : null) })
275        ] }),
276        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
277          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-sm font-medium", children: "Short Label" }),
278          /* @__PURE__ */ jsxRuntimeExports.jsx(Input, { value: editingGroup.shortName, onChange: (e) => setEditingGroup((g) => g ? { ...g, shortName: e.target.value } : null) })
279        ] }),
280        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
281          /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-sm font-medium", children: "Color" }),
282          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex gap-2 flex-wrap", children: COLOR_OPTIONS.map((c) => /* @__PURE__ */ jsxRuntimeExports.jsx(
283            "button",
284            {
285              className: `w-7 h-7 rounded-full border-2 transition-all ${editingGroup.color === c ? "border-foreground scale-110" : "border-transparent"}`,
286              style: { backgroundColor: c },
287              onClick: () => setEditingGroup((g) => g ? { ...g, color: c } : null)
288            },
289            c
290          )) })
291        ] })
292      ] }),
293      /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogFooter, { children: [
294        /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "outline", onClick: () => setEditingGroup(null), children: "Cancel" }),
295        /* @__PURE__ */ jsxRuntimeExports.jsx(
296          Button,
297          {
298            onClick: () => editingGroup && updateGroup.mutate({ groupId: editingGroup.groupId, name: editingGroup.name, shortName: editingGroup.shortName, color: editingGroup.color }),
299            disabled: updateGroup.isPending,
300            children: updateGroup.isPending ? "Saving…" : "Save Changes"
301          }
302        )
303      ] })
304    ] }) }),
305    /* @__PURE__ */ jsxRuntimeExports.jsx(AlertDialog, { open: !!deletingGroupId, onOpenChange: () => setDeletingGroupId(null), children: /* @__PURE__ */ jsxRuntimeExports.jsxs(AlertDialogContent, { children: [
306      /* @__PURE__ */ jsxRuntimeExports.jsxs(AlertDialogHeader, { children: [
307        /* @__PURE__ */ jsxRuntimeExports.jsx(AlertDialogTitle, { children: "Delete this group?" }),
308        /* @__PURE__ */ jsxRuntimeExports.jsx(AlertDialogDescription, { children: "All members will become solo producers. This cannot be undone." })
309      ] }),
310      /* @__PURE__ */ jsxRuntimeExports.jsxs(AlertDialogFooter, { children: [
311        /* @__PURE__ */ jsxRuntimeExports.jsx(AlertDialogCancel, { children: "Cancel" }),
312        /* @__PURE__ */ jsxRuntimeExports.jsx(
313          AlertDialogAction,
314          {
315            className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
316            onClick: () => deletingGroupId && deleteGroup.mutate({ groupId: deletingGroupId }),
317            children: "Delete Group"
318          }
319        )
320      ] })
321    ] }) })
322  ] });
323}
324
325export { AgencyGroupManager as default };

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.