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.